Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/app/inputhdl.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 <iterator>
21
#include <memory>
22
#include <string_view>
23
24
#include <inputhdl.hxx>
25
#include <scitems.hxx>
26
#include <editeng/eeitem.hxx>
27
28
#include <sfx2/app.hxx>
29
#include <editeng/acorrcfg.hxx>
30
#include <formula/errorcodes.hxx>
31
#include <editeng/adjustitem.hxx>
32
#include <editeng/brushitem.hxx>
33
#include <svtools/colorcfg.hxx>
34
#include <editeng/colritem.hxx>
35
#include <editeng/editobj.hxx>
36
#include <editeng/editstat.hxx>
37
#include <editeng/editview.hxx>
38
#include <editeng/langitem.hxx>
39
#include <editeng/svxacorr.hxx>
40
#include <editeng/unolingu.hxx>
41
#include <editeng/wghtitem.hxx>
42
#include <editeng/justifyitem.hxx>
43
#include <editeng/misspellrange.hxx>
44
#include <sfx2/bindings.hxx>
45
#include <sfx2/viewfrm.hxx>
46
#include <sfx2/docfile.hxx>
47
#include <sfx2/printer.hxx>
48
#include <svl/numformat.hxx>
49
#include <svl/zforlist.hxx>
50
#include <svtools/langtab.hxx>
51
#include <unotools/localedatawrapper.hxx>
52
#include <unotools/charclass.hxx>
53
#include <utility>
54
#include <vcl/help.hxx>
55
#include <vcl/jsdialog/executor.hxx>
56
#include <vcl/commandevent.hxx>
57
#include <vcl/cursor.hxx>
58
#include <vcl/settings.hxx>
59
#include <vcl/svapp.hxx>
60
#include <tools/urlobj.hxx>
61
#include <tools/json_writer.hxx>
62
#include <formula/formulahelper.hxx>
63
#include <formula/funcvarargs.h>
64
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
65
#include <comphelper/lok.hxx>
66
#include <osl/diagnose.h>
67
68
#include <attrib.hxx>
69
#include <inputwin.hxx>
70
#include <tabvwsh.hxx>
71
#include <docsh.hxx>
72
#include <scmod.hxx>
73
#include <formulaopt.hxx>
74
#include <uiitems.hxx>
75
#include <global.hxx>
76
#include <sc.hrc>
77
#include <globstr.hrc>
78
#include <scresid.hxx>
79
#include <patattr.hxx>
80
#include <viewdata.hxx>
81
#include <document.hxx>
82
#include <docpool.hxx>
83
#include <editutil.hxx>
84
#include <appoptio.hxx>
85
#include <docoptio.hxx>
86
#include <validat.hxx>
87
#include <rfindlst.hxx>
88
#include <inputopt.hxx>
89
#include <simpleformulacalc.hxx>
90
#include <compiler.hxx>
91
#include <editable.hxx>
92
#include <funcdesc.hxx>
93
#include <markdata.hxx>
94
#include <tokenarray.hxx>
95
#include <gridwin.hxx>
96
#include <output.hxx>
97
#include <fillinfo.hxx>
98
99
using namespace formula;
100
101
namespace {
102
103
ScTypedCaseStrSet::const_iterator findText(
104
    const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos,
105
    const OUString& rStart, OUString& rResult, bool bBack)
106
0
{
107
0
    auto lIsMatch = [&rStart](const ScTypedStrData& rData) {
108
0
        return (rData.GetStringType() != ScTypedStrData::Value) && ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()); };
109
110
0
    if (bBack) // Backwards
111
0
    {
112
0
        ScTypedCaseStrSet::const_reverse_iterator it = rDataSet.rbegin(), itEnd = rDataSet.rend();
113
0
        if (itPos != rDataSet.end())
114
0
        {
115
0
            size_t nPos = std::distance(rDataSet.begin(), itPos);
116
0
            size_t nRPos = rDataSet.size() - 1 - nPos;
117
0
            std::advance(it, nRPos);
118
0
            ++it;
119
0
        }
120
121
0
        it = std::find_if(it, itEnd, lIsMatch);
122
0
        if (it != itEnd)
123
0
        {
124
0
            rResult = it->GetString();
125
0
            return (++it).base(); // convert the reverse iterator back to iterator.
126
0
        }
127
0
    }
128
0
    else // Forwards
129
0
    {
130
0
        ScTypedCaseStrSet::const_iterator it = rDataSet.begin(), itEnd = rDataSet.end();
131
0
        if (itPos != itEnd)
132
0
        {
133
0
            it = std::next(itPos);
134
0
        }
135
136
0
        it = std::find_if(it, itEnd, lIsMatch);
137
0
        if (it != itEnd)
138
0
        {
139
0
            rResult = it->GetString();
140
0
            return it;
141
0
        }
142
0
    }
143
144
0
    return rDataSet.end(); // no matching text found
145
0
}
146
147
OUString getExactMatch(const ScTypedCaseStrSet& rDataSet, const OUString& rString)
148
0
{
149
0
    auto it = std::find_if(rDataSet.begin(), rDataSet.end(),
150
0
        [&rString](const ScTypedStrData& rData) {
151
0
            return (rData.GetStringType() != ScTypedStrData::Value)
152
0
                && ScGlobal::GetTransliteration().isEqual(rData.GetString(), rString);
153
0
        });
154
0
    if (it != rDataSet.end())
155
0
        return it->GetString();
156
0
    return rString;
157
0
}
158
159
// This assumes that rResults is a sorted ring w.r.t ScTypedStrData::LessCaseInsensitive() or
160
// in the reverse direction, whose origin is specified by nRingOrigin.
161
sal_Int32 getLongestCommonPrefixLength(const std::vector<OUString>& rResults, std::u16string_view aUserEntry, sal_Int32 nRingOrigin)
162
0
{
163
0
    sal_Int32 nResults = rResults.size();
164
0
    if (!nResults)
165
0
        return 0;
166
167
0
    if (nResults == 1)
168
0
        return rResults[0].getLength();
169
170
0
    sal_Int32 nMinLen = aUserEntry.size();
171
0
    sal_Int32 nLastIdx = nRingOrigin ? nRingOrigin - 1 : nResults - 1;
172
0
    const OUString& rFirst = rResults[nRingOrigin];
173
0
    const OUString& rLast = rResults[nLastIdx];
174
0
    const sal_Int32 nMaxLen = std::min(rFirst.getLength(), rLast.getLength());
175
176
0
    for (sal_Int32 nLen = nMaxLen; nLen > nMinLen; --nLen)
177
0
    {
178
0
        if (ScGlobal::GetTransliteration().isMatch(rFirst.copy(0, nLen), rLast))
179
0
            return nLen;
180
0
    }
181
182
0
    return nMinLen;
183
0
}
184
185
ScTypedCaseStrSet::const_iterator findTextAll(
186
    const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos,
187
    const OUString& rStart, ::std::vector< OUString > &rResultVec, bool bBack, sal_Int32* pLongestPrefixLen = nullptr)
188
0
{
189
0
    rResultVec.clear(); // clear contents
190
191
0
    if (!rDataSet.size())
192
0
        return rDataSet.end();
193
194
0
    sal_Int32 nRingOrigin = 0;
195
0
    size_t nCount = 0;
196
0
    ScTypedCaseStrSet::const_iterator retit;
197
0
    if ( bBack ) // Backwards
198
0
    {
199
0
        ScTypedCaseStrSet::const_reverse_iterator it, itEnd;
200
0
        if ( itPos == rDataSet.end() )
201
0
        {
202
0
            it = rDataSet.rend();
203
0
            --it;
204
0
            itEnd = it;
205
0
        }
206
0
        else
207
0
        {
208
0
            it = rDataSet.rbegin();
209
0
            size_t nPos = std::distance(rDataSet.begin(), itPos);
210
0
            size_t nRPos = rDataSet.size() - 1 - nPos; // if itPos == rDataSet.end(), then nRPos = -1
211
0
            std::advance(it, nRPos);
212
0
            if ( it == rDataSet.rend() )
213
0
                it = rDataSet.rbegin();
214
0
            itEnd = it;
215
0
        }
216
0
        bool bFirstTime = true;
217
218
0
        while ( it != itEnd || bFirstTime )
219
0
        {
220
0
            ++it;
221
0
            if ( it == rDataSet.rend() ) // go to the first if reach the end
222
0
            {
223
0
                it = rDataSet.rbegin();
224
0
                nRingOrigin = nCount;
225
0
            }
226
227
0
            if ( bFirstTime )
228
0
                bFirstTime = false;
229
0
            const ScTypedStrData& rData = *it;
230
0
            if ( rData.GetStringType() == ScTypedStrData::Value )
231
                // skip values
232
0
                continue;
233
234
0
            if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) )
235
                // not a match
236
0
                continue;
237
238
0
            rResultVec.push_back(rData.GetString()); // set the match data
239
0
            if ( nCount == 0 ) // convert the reverse iterator back to iterator.
240
0
            {
241
                // actually we want to do "retit = it;".
242
0
                retit = rDataSet.begin();
243
0
                size_t nRPos = std::distance(rDataSet.rbegin(), it);
244
0
                size_t nPos = rDataSet.size() - 1 - nRPos;
245
0
                std::advance(retit, nPos);
246
0
            }
247
0
            ++nCount;
248
0
        }
249
0
    }
250
0
    else // Forwards
251
0
    {
252
0
        ScTypedCaseStrSet::const_iterator it, itEnd;
253
0
        it = itPos;
254
0
        if ( it == rDataSet.end() )
255
0
            it = --rDataSet.end();
256
0
        itEnd = it;
257
0
        bool bFirstTime = true;
258
259
0
        while ( it != itEnd || bFirstTime )
260
0
        {
261
0
            ++it;
262
0
            if ( it == rDataSet.end() ) // go to the first if reach the end
263
0
            {
264
0
                it = rDataSet.begin();
265
0
                nRingOrigin = nCount;
266
0
            }
267
268
0
            if ( bFirstTime )
269
0
                bFirstTime = false;
270
0
            const ScTypedStrData& rData = *it;
271
0
            if ( rData.GetStringType() == ScTypedStrData::Value )
272
                // skip values
273
0
                continue;
274
275
0
            if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) )
276
                // not a match
277
0
                continue;
278
279
0
            rResultVec.push_back(rData.GetString()); // set the match data
280
0
            if ( nCount == 0 )
281
0
                retit = it; // remember first match iterator
282
0
            ++nCount;
283
0
        }
284
0
    }
285
286
0
    if (pLongestPrefixLen)
287
0
    {
288
0
        if (nRingOrigin >= static_cast<sal_Int32>(nCount))
289
0
        {
290
            // All matches were picked when rDataSet was read in one direction.
291
0
            nRingOrigin = 0;
292
0
        }
293
        // rResultsVec is a sorted ring with nRingOrigin "origin".
294
        // The direction of sorting is not important for getLongestCommonPrefixLength.
295
0
        *pLongestPrefixLen = getLongestCommonPrefixLength(rResultVec, rStart, nRingOrigin);
296
0
    }
297
298
0
    if ( nCount > 0 ) // at least one function has matched
299
0
        return retit;
300
0
    return rDataSet.end(); // no matching text found
301
0
}
302
303
}
304
305
void ScInputHandler::SendReferenceMarks( const SfxViewShell* pViewShell,
306
                            const std::vector<ReferenceMark>& rReferenceMarks )
307
0
{
308
0
    if ( !pViewShell )
309
0
        return;
310
311
0
    bool bSend = false;
312
313
0
    std::stringstream ss;
314
315
0
    ss << "{ \"marks\": [ ";
316
317
0
    for ( size_t i = 0; i < rReferenceMarks.size(); i++ )
318
0
    {
319
0
        if ( rReferenceMarks[i].Is() )
320
0
        {
321
0
            if ( bSend )
322
0
                ss << ", ";
323
324
0
            ss << "{ \"rectangle\": \""
325
0
               << rReferenceMarks[i].nX << ", "
326
0
               << rReferenceMarks[i].nY << ", "
327
0
               << rReferenceMarks[i].nWidth << ", "
328
0
               << rReferenceMarks[i].nHeight << "\", "
329
0
                  "\"color\": \"" << rReferenceMarks[i].aColor.AsRGBHexString() << "\", "
330
0
                  "\"part\": \"" << rReferenceMarks[i].nTab << "\" } ";
331
332
0
            bSend = true;
333
0
        }
334
0
    }
335
336
0
    ss <<  " ] }";
337
338
0
    OString aPayload( ss.str() );
339
0
    pViewShell->libreOfficeKitViewCallback(
340
0
                LOK_CALLBACK_REFERENCE_MARKS, aPayload );
341
0
}
342
343
static inline void incPos( const sal_Unicode c, sal_Int32& rPos, ESelection& rSel )
344
0
{
345
0
    ++rPos;
346
0
    if (c == '\n')
347
0
    {
348
0
        ++rSel.end.nPara;
349
0
        rSel.end.nIndex = 0;
350
0
    }
351
0
    else
352
0
    {
353
0
        ++rSel.end.nIndex;
354
0
    }
355
0
}
356
357
void ScInputHandler::InitRangeFinder( const OUString& rFormula )
358
0
{
359
0
    DeleteRangeFinder();
360
0
    if (!pActiveViewSh || !ScModule::get()->GetInputOptions().GetRangeFinder())
361
0
        return;
362
0
    ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
363
0
    ScDocument& rDoc = pDocSh->GetDocument();
364
0
    const sal_Unicode cSheetSep = rDoc.GetSheetSeparator();
365
366
0
    OUString aDelimiters = ScEditUtil::ModifyDelimiters(u" !~%\"\t\n"_ustr);
367
        // delimiters (in addition to ScEditUtil): only characters that are
368
        // allowed in formulas next to references and the quotation mark (so
369
        // string constants can be skipped)
370
371
0
    sal_Int32 nColon = aDelimiters.indexOf( ':' );
372
0
    if ( nColon != -1 )
373
0
        aDelimiters = aDelimiters.replaceAt( nColon, 1, u""); // Delimiter without colon
374
0
    sal_Int32 nDot = aDelimiters.indexOf(cSheetSep);
375
0
    if ( nDot != -1 )
376
0
        aDelimiters = aDelimiters.replaceAt( nDot, 1 , u""); // Delimiter without dot
377
378
0
    const sal_Unicode* pChar = rFormula.getStr();
379
0
    sal_Int32 nLen = rFormula.getLength();
380
0
    sal_Int32 nPos = 0;
381
0
    sal_Int32 nStart = 0;
382
0
    ESelection aSel;
383
0
    sal_uInt16 nCount = 0;
384
    // Maximum Ranges in RangeFinder
385
0
    constexpr sal_uInt16 RANGEFIND_MAX = 128;
386
0
    ScRange aRange;
387
0
    while ( nPos < nLen && nCount < RANGEFIND_MAX )
388
0
    {
389
        // Skip separator
390
0
        while ( nPos<nLen && ScGlobal::UnicodeStrChr( aDelimiters.getStr(), pChar[nPos] ) )
391
0
        {
392
0
            if ( pChar[nPos] == '"' )                       // String
393
0
            {
394
0
                incPos( pChar[nPos], nPos, aSel);
395
0
                while (nPos<nLen && pChar[nPos] != '"')     // Skip until end
396
0
                    incPos( pChar[nPos], nPos, aSel);
397
0
            }
398
0
            incPos( pChar[nPos], nPos, aSel);  // Separator or closing quote
399
0
        }
400
401
        // Text between separators. We only consider within one line/paragraph.
402
0
        aSel.CollapseToEnd();
403
0
        nStart = nPos;
404
0
handle_r1c1:
405
0
        {
406
0
            bool bSingleQuoted = false;
407
0
            while (nPos < nLen)
408
0
            {
409
                // tdf#114113: handle addresses with quoted sheet names like "'Sheet 1'.A1"
410
                // Literal single quotes in sheet names are masked by another single quote
411
0
                if (pChar[nPos] == '\'')
412
0
                {
413
0
                    bSingleQuoted = !bSingleQuoted;
414
0
                }
415
0
                else if (!bSingleQuoted) // Get everything in single quotes, including separators
416
0
                {
417
0
                    if (ScGlobal::UnicodeStrChr(aDelimiters.getStr(), pChar[nPos]))
418
0
                        break;
419
0
                }
420
0
                incPos( pChar[nPos], nPos, aSel);
421
0
            }
422
0
        }
423
424
        // for R1C1 '-' in R[-]... or C[-]... are not delimiters
425
        // Nothing heroic here to ensure that there are '[]' around a negative
426
        // integer.  we need to clean up this code.
427
0
        if( nPos < nLen && nPos > 0 &&
428
0
            '-' == pChar[nPos] && '[' == pChar[nPos-1] &&
429
0
            formula::FormulaGrammar::CONV_XL_R1C1 == rDoc.GetAddressConvention() )
430
0
        {
431
0
            incPos( pChar[nPos], nPos, aSel);
432
0
            goto handle_r1c1;
433
0
        }
434
435
0
        if ( nPos > nStart )
436
0
        {
437
0
            OUString aTest = rFormula.copy( nStart, nPos-nStart );
438
0
            const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
439
0
            ScRefFlags nFlags = aRange.ParseAny( aTest, rDoc, aAddrDetails );
440
0
            if ( nFlags & ScRefFlags::VALID )
441
0
            {
442
                //  Set tables if not specified
443
0
                if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO)
444
0
                    aRange.aStart.SetTab( pActiveViewSh->GetViewData().CurrentTabForData() );
445
0
                if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO)
446
0
                    aRange.aEnd.SetTab( aRange.aStart.Tab() );
447
448
0
                if ( ( nFlags & (ScRefFlags::COL2_VALID|ScRefFlags::ROW2_VALID|ScRefFlags::TAB2_VALID) ) ==
449
0
                                 ScRefFlags::ZERO )
450
0
                {
451
                    // #i73766# if a single ref was parsed, set the same "abs" flags for ref2,
452
                    // so Format doesn't output a double ref because of different flags.
453
0
                    ScRefFlags nAbsFlags = nFlags & (ScRefFlags::COL_ABS|ScRefFlags::ROW_ABS|ScRefFlags::TAB_ABS);
454
0
                    applyStartToEndFlags(nFlags, nAbsFlags);
455
0
                }
456
457
0
                if (!nCount)
458
0
                {
459
0
                    mpEditEngine->SetUpdateLayout( false );
460
0
                    pRangeFindList.reset(new ScRangeFindList( pDocSh->GetTitle() ));
461
0
                }
462
463
0
                Color nColor = pRangeFindList->Insert( ScRangeFindData( aRange, nFlags, aSel));
464
465
0
                SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
466
0
                aSet.Put( SvxColorItem( nColor, EE_CHAR_COLOR ) );
467
0
                mpEditEngine->QuickSetAttribs( aSet, aSel );
468
0
                ++nCount;
469
0
            }
470
0
        }
471
472
        // Do not skip last separator; could be a quote (?)
473
0
    }
474
475
0
    UpdateLokReferenceMarks();
476
477
0
    if (nCount)
478
0
    {
479
0
        mpEditEngine->SetUpdateLayout( true );
480
481
0
        pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) );
482
0
    }
483
0
}
484
485
ReferenceMark ScInputHandler::GetReferenceMark( const ScViewData& rViewData, ScDocShell& rDocSh,
486
                                    tools::Long nX1, tools::Long nX2, tools::Long nY1, tools::Long nY2,
487
                                    tools::Long nTab, const Color& rColor )
488
0
{
489
0
    ScSplitPos eWhich = rViewData.GetActivePart();
490
491
    // This method is LOK specific.
492
0
    if (comphelper::LibreOfficeKit::isCompatFlagSet(
493
0
            comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
494
0
    {
495
0
        SCCOL nCol1 = nX1, nCol2 = nX2;
496
0
        SCROW nRow1 = nY1, nRow2 = nY2;
497
0
        ScDocument& rDoc = rDocSh.GetDocument();
498
499
0
        PutInOrder(nCol1, nCol2);
500
0
        PutInOrder(nRow1, nRow2);
501
502
0
        if (nCol1 == nCol2 && nRow1 == nRow2)
503
0
            rDoc.ExtendMerge(nCol1, nRow1, nCol2, nRow2, nTab);
504
0
        else if (rDoc.HasAttrib(nCol2, nRow2, nTab, HasAttrFlags::Merged))
505
0
            rDoc.ExtendMerge(nCol2, nRow2, nCol2, nRow2, nTab);
506
507
0
        Point aTopLeft = rViewData.GetPrintTwipsPos(nCol1, nRow1);
508
0
        Point aBottomRight = rViewData.GetPrintTwipsPos(nCol2 + 1, nRow2 + 1);
509
0
        tools::Long nSizeX = aBottomRight.X() - aTopLeft.X() - 1;
510
0
        tools::Long nSizeY = aBottomRight.Y() - aTopLeft.Y() - 1;
511
512
0
        return ReferenceMark(aTopLeft.X(), aTopLeft.Y(), nSizeX, nSizeY, nTab, rColor);
513
0
    }
514
515
0
    Point aScrPos = rViewData.GetScrPos( nX1, nY1, eWhich );
516
0
    tools::Long nScrX = aScrPos.X();
517
0
    tools::Long nScrY = aScrPos.Y();
518
519
0
    double nPPTX = rViewData.GetPPTX();
520
0
    double nPPTY = rViewData.GetPPTY();
521
522
0
    Fraction aZoomX = rViewData.GetZoomX();
523
0
    Fraction aZoomY = rViewData.GetZoomY();
524
525
0
    ScTableInfo aTabInfo(nY1, nY2, true);
526
0
    rDocSh.GetDocument().FillInfo( aTabInfo, nX1, nY1, nX2, nY2,
527
0
                                    nTab, nPPTX, nPPTY, false, false );
528
529
0
    ScOutputData aOutputData( nullptr, OUTTYPE_WINDOW, aTabInfo,
530
0
                              &( rDocSh.GetDocument() ), nTab,
531
0
                              nScrX, nScrY,
532
0
                              nX1, nY1, nX2, nY2,
533
0
                              nPPTX, nPPTY,
534
0
                              &aZoomX, &aZoomY );
535
536
0
    return aOutputData.FillReferenceMark( nX1, nY1, nX2, nY2,
537
0
                                          rColor );
538
0
}
539
540
void ScInputHandler::UpdateLokReferenceMarks()
541
0
{
542
0
    if ( !comphelper::LibreOfficeKit::isActive())
543
0
        return;
544
545
0
    ScTabViewShell* pShell = pActiveViewSh ? pActiveViewSh
546
0
                                : dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
547
548
0
    if (!pShell)
549
0
        return;
550
551
0
    ScViewData& rViewData = pShell->GetViewData();
552
0
    ScDocShell* pDocSh = rViewData.GetDocShell();
553
0
    ScRangeFindList* pRangeFinder = GetRangeFindList();
554
555
0
    if ( !pRangeFinder && !rViewData.IsRefMode() )
556
0
        return;
557
558
0
    sal_uInt16 nAdditionalMarks = 0;
559
0
    std::vector<ReferenceMark> aReferenceMarks( 1 );
560
561
0
    if ( rViewData.IsRefMode() )
562
0
    {
563
0
        nAdditionalMarks = 1;
564
565
0
        const svtools::ColorConfig& rColorCfg = ScModule::get()->GetColorConfig();
566
0
        Color aRefColor( rColorCfg.GetColorValue( svtools::CALCREFERENCE ).nColor );
567
0
        tools::Long nX1 = rViewData.GetRefStartX();
568
0
        tools::Long nX2 = rViewData.GetRefEndX();
569
0
        tools::Long nY1 = rViewData.GetRefStartY();
570
0
        tools::Long nY2 = rViewData.GetRefEndY();
571
0
        tools::Long nTab = rViewData.GetRefStartZ();
572
573
0
        if (rViewData.GetRefEndZ() == rViewData.CurrentTabForData())
574
0
            nTab = rViewData.GetRefEndZ();
575
576
0
        PutInOrder(nX1, nX2);
577
0
        PutInOrder(nY1, nY2);
578
579
0
        aReferenceMarks[0] = ScInputHandler::GetReferenceMark( rViewData, *pDocSh,
580
0
                                                   nX1, nX2, nY1, nY2,
581
0
                                                   nTab, aRefColor );
582
0
    }
583
584
0
    sal_uInt16 nCount = pRangeFinder ?
585
0
        ( static_cast<sal_uInt16>( pRangeFinder->Count() ) + nAdditionalMarks ) : nAdditionalMarks;
586
0
    aReferenceMarks.resize( nCount );
587
588
0
    if ( nCount && pRangeFinder && !pRangeFinder->IsHidden() &&
589
0
         pRangeFinder->GetDocName() == pDocSh->GetTitle() )
590
0
    {
591
0
        for (sal_uInt16 i = 0; i < nCount - nAdditionalMarks; i++)
592
0
        {
593
0
            ScRangeFindData& rData = pRangeFinder->GetObject( i );
594
0
            ScRange aRef = rData.aRef;
595
0
            aRef.PutInOrder();
596
597
0
            tools::Long nX1 = aRef.aStart.Col();
598
0
            tools::Long nX2 = aRef.aEnd.Col();
599
0
            tools::Long nY1 = aRef.aStart.Row();
600
0
            tools::Long nY2 = aRef.aEnd.Row();
601
0
            tools::Long nTab = aRef.aStart.Tab();
602
603
0
            aReferenceMarks[i + nAdditionalMarks] = ScInputHandler::GetReferenceMark( rViewData, *pDocSh,
604
0
                                                                          nX1, nX2, nY1, nY2,
605
0
                                                                          nTab, rData.nColor );
606
607
0
            ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks );
608
0
        }
609
0
    }
610
0
    else if ( nCount )
611
0
    {
612
0
        ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks );
613
0
    }
614
0
    else
615
0
    {
616
        // Clear
617
0
        aReferenceMarks.clear();
618
0
        ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks );
619
0
    }
620
0
}
621
622
void ScInputHandler::SetDocumentDisposing( bool b )
623
0
{
624
0
    mbDocumentDisposing = b;
625
0
}
626
627
static void lcl_Replace( EditView* pView, const OUString& rNewStr, const ESelection& rOldSel )
628
0
{
629
0
    if ( !pView )
630
0
        return;
631
632
0
    ESelection aOldSel = pView->GetSelection();
633
0
    if (aOldSel.HasRange())
634
0
        pView->SetSelection(ESelection(aOldSel.end));
635
636
0
    EditEngine& rEngine = pView->getEditEngine();
637
0
    rEngine.QuickInsertText( rNewStr, rOldSel );
638
639
    // Dummy InsertText for Update and Paint
640
    // To do that we need to cancel the selection from above (before QuickInsertText)
641
0
    pView->InsertText( OUString() );
642
643
0
    pView->SetSelection(ESelection::AtEnd()); // Set cursor to the end
644
0
}
645
646
void ScInputHandler::UpdateRange( sal_uInt16 nIndex, const ScRange& rNew )
647
0
{
648
0
    ScTabViewShell* pDocView = pRefViewSh ? pRefViewSh : pActiveViewSh;
649
0
    if ( pDocView && pRangeFindList && nIndex < pRangeFindList->Count() )
650
0
    {
651
0
        ScRangeFindData& rData = pRangeFindList->GetObject( nIndex );
652
0
        Color nNewColor = pRangeFindList->FindColor( rNew, nIndex );
653
654
0
        ScRange aJustified = rNew;
655
0
        aJustified.PutInOrder(); // Always display Ref in the Formula the right way
656
0
        ScDocument& rDoc = pDocView->GetViewData().GetDocument();
657
0
        const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
658
0
        OUString aNewStr(aJustified.Format(rDoc, rData.nFlags, aAddrDetails));
659
0
        SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
660
661
0
        DataChanging();
662
663
0
        lcl_Replace( pTopView, aNewStr, rData.maSel );
664
0
        lcl_Replace( pTableView, aNewStr, rData.maSel );
665
666
        // We are within one paragraph.
667
0
        const sal_Int32 nDiff = aNewStr.getLength() - (rData.maSel.end.nIndex - rData.maSel.start.nIndex);
668
0
        rData.maSel.end.nIndex += nDiff;
669
670
0
        aSet.Put( SvxColorItem( nNewColor, EE_CHAR_COLOR ) );
671
0
        mpEditEngine->QuickSetAttribs( aSet, rData.maSel );
672
673
0
        bInRangeUpdate = true;
674
0
        DataChanged();
675
0
        bInRangeUpdate = false;
676
677
0
        rData.aRef = rNew;
678
0
        rData.nColor = nNewColor;
679
680
0
        if (nDiff)
681
0
        {
682
0
            const size_t nCount = pRangeFindList->Count();
683
0
            for (size_t i = nIndex + 1; i < nCount; ++i)
684
0
            {
685
0
                ScRangeFindData& rNext = pRangeFindList->GetObject( i );
686
0
                if (rNext.maSel.start.nPara != rData.maSel.start.nPara)
687
0
                    break;
688
689
0
                rNext.maSel.start.nIndex += nDiff;
690
0
                rNext.maSel.end.nIndex   += nDiff;
691
0
            }
692
0
        }
693
694
0
        EditView* pActiveView = pTopView ? pTopView : pTableView;
695
0
        pActiveView->ShowCursor( false );
696
0
    }
697
0
    else
698
0
    {
699
0
        OSL_FAIL("UpdateRange: we're missing something");
700
0
    }
701
0
}
702
703
void ScInputHandler::DeleteRangeFinder()
704
0
{
705
0
    ScTabViewShell* pPaintView = pRefViewSh ? pRefViewSh : pActiveViewSh;
706
0
    if ( pRangeFindList && pPaintView )
707
0
    {
708
0
        ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
709
0
        pRangeFindList->SetHidden(true);
710
0
        pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) );  // Steal
711
0
        pRangeFindList.reset();
712
0
    }
713
0
}
714
715
static OUString GetEditText(const EditEngine* pEng)
716
0
{
717
0
    return ScEditUtil::GetMultilineString(*pEng);
718
0
}
719
720
static void lcl_RemoveTabs(OUString& rStr)
721
0
{
722
0
    rStr = rStr.replace('\t', ' ');
723
0
}
724
725
static void lcl_RemoveLineEnd(OUString& rStr)
726
0
{
727
0
    rStr = convertLineEnd(rStr, LINEEND_LF);
728
0
    rStr = rStr.replace('\n', ' ');
729
0
}
730
731
static sal_Int32 lcl_MatchParenthesis( const OUString& rStr, sal_Int32 nPos )
732
0
{
733
0
    int nDir;
734
0
    sal_Unicode c1, c2 = 0;
735
0
    c1 = rStr[nPos];
736
0
    switch ( c1 )
737
0
    {
738
0
    case '(' :
739
0
        c2 = ')';
740
0
        nDir = 1;
741
0
        break;
742
0
    case ')' :
743
0
        c2 = '(';
744
0
        nDir = -1;
745
0
        break;
746
0
    case '<' :
747
0
        c2 = '>';
748
0
        nDir = 1;
749
0
        break;
750
0
    case '>' :
751
0
        c2 = '<';
752
0
        nDir = -1;
753
0
        break;
754
0
    case '{' :
755
0
        c2 = '}';
756
0
        nDir = 1;
757
0
        break;
758
0
    case '}' :
759
0
        c2 = '{';
760
0
        nDir = -1;
761
0
        break;
762
0
    case '[' :
763
0
        c2 = ']';
764
0
        nDir = 1;
765
0
        break;
766
0
    case ']' :
767
0
        c2 = '[';
768
0
        nDir = -1;
769
0
        break;
770
0
    default:
771
0
        nDir = 0;
772
0
    }
773
0
    if ( !nDir )
774
0
        return -1;
775
0
    sal_Int32 nLen = rStr.getLength();
776
0
    const sal_Unicode* p0 = rStr.getStr();
777
0
    const sal_Unicode* p;
778
0
    const sal_Unicode* p1;
779
0
    sal_uInt16 nQuotes = 0;
780
0
    if ( nPos < nLen / 2 )
781
0
    {
782
0
        p = p0;
783
0
        p1 = p0 + nPos;
784
0
    }
785
0
    else
786
0
    {
787
0
        p = p0 + nPos;
788
0
        p1 = p0 + nLen;
789
0
    }
790
0
    while ( p < p1 )
791
0
    {
792
0
        if ( *p++ == '\"' )
793
0
            nQuotes++;
794
0
    }
795
    // Odd number of quotes that we find ourselves in a string
796
0
    bool bLookInString = ((nQuotes % 2) != 0);
797
0
    bool bInString = bLookInString;
798
0
    p = p0 + nPos;
799
0
    p1 = (nDir < 0 ? p0 : p0 + nLen) ;
800
0
    sal_uInt16 nLevel = 1;
801
0
    while ( p != p1 && nLevel )
802
0
    {
803
0
        p += nDir;
804
0
        if ( *p == '\"' )
805
0
        {
806
0
            bInString = !bInString;
807
0
            if ( bLookInString && !bInString )
808
0
                p = p1; // That's it then
809
0
        }
810
0
        else if ( bInString == bLookInString )
811
0
        {
812
0
            if ( *p == c1 )
813
0
                nLevel++;
814
0
            else if ( *p == c2 )
815
0
                nLevel--;
816
0
        }
817
0
    }
818
0
    if ( nLevel )
819
0
        return -1;
820
0
    return static_cast<sal_Int32>(p - p0);
821
0
}
822
823
ScInputHandler::ScInputHandler()
824
0
    :   pInputWin( nullptr ),
825
0
        pTableView( nullptr ),
826
0
        pTopView( nullptr ),
827
0
        pTipVisibleParent( nullptr ),
828
0
        nTipVisible( nullptr ),
829
0
        pTipVisibleSecParent( nullptr ),
830
0
        nTipVisibleSec( nullptr ),
831
0
        nFormSelStart( 0 ),
832
0
        nFormSelEnd( 0 ),
833
0
        nCellPercentFormatDecSep( 0 ),
834
0
        nAutoPar( 0 ),
835
0
        eMode( SC_INPUT_NONE ),
836
0
        bUseTab( false ),
837
0
        bTextValid( true ),
838
0
        bModified( false ),
839
0
        bSelIsRef( false ),
840
0
        bFormulaMode( false ),
841
0
        bInRangeUpdate( false ),
842
0
        bParenthesisShown( false ),
843
0
        bCreatingFuncView( false ),
844
0
        bInEnterHandler( false ),
845
0
        bCommandErrorShown( false ),
846
0
        bInOwnChange( false ),
847
0
        bProtected( false ),
848
0
        bLastIsSymbol( false ),
849
0
        mbDocumentDisposing(false),
850
0
        mbPartialPrefix(false),
851
0
        mbEditingExistingContent(false),
852
0
        nValidation( 0 ),
853
0
        eAttrAdjust( SvxCellHorJustify::Standard ),
854
0
        aScaleX( 1,1 ),
855
0
        aScaleY( 1,1 ),
856
0
        pRefViewSh( nullptr ),
857
0
        pLastPattern( nullptr )
858
0
{
859
    //  The InputHandler is constructed with the view, so SfxViewShell::Current
860
    //  doesn't have the right view yet. pActiveViewSh is updated in NotifyChange.
861
0
    pActiveViewSh = nullptr;
862
863
    //  Bindings (only still used for Invalidate) are retrieved if needed on demand
864
865
0
    pDelayTimer.reset( new Timer( "ScInputHandlerDelay timer" ) );
866
0
    pDelayTimer->SetTimeout( 500 ); // 500 ms delay
867
0
    pDelayTimer->SetInvokeHandler( LINK( this, ScInputHandler, DelayTimer ) );
868
0
}
869
870
ScInputHandler::~ScInputHandler()
871
0
{
872
    //  If this is the application InputHandler, the dtor is called after SfxApplication::Main,
873
    //  thus we can't rely on any Sfx functions
874
0
    if (!mbDocumentDisposing) // inplace
875
0
        EnterHandler(); // Finish input
876
877
0
    if (ScModule* mod = ScModule::get(); mod->GetRefInputHdl() == this)
878
0
        mod->SetRefInputHdl(nullptr);
879
880
0
    if ( pInputWin && pInputWin->GetInputHandler() == this )
881
0
        pInputWin->SetInputHandler( nullptr );
882
0
}
883
884
void ScInputHandler::SetRefScale( const Fraction& rX, const Fraction& rY )
885
0
{
886
0
    if ( rX != aScaleX || rY != aScaleY )
887
0
    {
888
0
        aScaleX = rX;
889
0
        aScaleY = rY;
890
0
        if (mpEditEngine)
891
0
        {
892
0
            MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY );
893
0
            mpEditEngine->SetRefMapMode( aMode );
894
0
        }
895
0
    }
896
0
}
897
898
void ScInputHandler::UpdateRefDevice()
899
0
{
900
0
    if (!mpEditEngine)
901
0
        return;
902
903
0
    bool bTextWysiwyg = ScModule::get()->GetInputOptions().GetTextWysiwyg();
904
0
    if ( bTextWysiwyg && pActiveViewSh )
905
0
        mpEditEngine->SetRefDevice( pActiveViewSh->GetViewData().GetDocument().GetPrinter() );
906
0
    else
907
0
        mpEditEngine->SetRefDevice( nullptr );
908
909
0
    MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY );
910
0
    mpEditEngine->SetRefMapMode( aMode );
911
912
    //  SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev,
913
    //  so the DigitLanguage can be safely modified (might use an own VDev instead of NULL).
914
0
    if ( !( bTextWysiwyg && pActiveViewSh ) )
915
0
    {
916
0
        mpEditEngine->GetRefDevice()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
917
0
    }
918
0
}
919
920
void ScInputHandler::ImplCreateEditEngine()
921
0
{
922
0
    if ( mpEditEngine )
923
0
        return;
924
925
    // we cannot create a properly initialised EditEngine until we have a document
926
0
    assert( pActiveViewSh );
927
0
    ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
928
0
    mpEditEngine = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEditEnginePool());
929
0
    mpEditEngine->SetWordDelimiters( ScEditUtil::ModifyDelimiters( mpEditEngine->GetWordDelimiters() ) );
930
0
    UpdateRefDevice();      // also sets MapMode
931
0
    mpEditEngine->SetPaperSize( Size( 1000000, 1000000 ) );
932
0
    pEditDefaults.reset( new SfxItemSet( mpEditEngine->GetEmptyItemSet() ) );
933
934
0
    mpEditEngine->SetControlWord( mpEditEngine->GetControlWord() | EEControlBits::AUTOCORRECT );
935
0
    mpEditEngine->SetReplaceLeadingSingleQuotationMark( false );
936
0
    mpEditEngine->SetModifyHdl( LINK( this, ScInputHandler, ModifyHdl ) );
937
0
}
938
939
void ScInputHandler::UpdateAutoCorrFlag()
940
0
{
941
0
    EEControlBits nCntrl = mpEditEngine->GetControlWord();
942
0
    EEControlBits nOld = nCntrl;
943
944
    // Don't use pLastPattern here (may be invalid because of AutoStyle)
945
0
    bool bDisable = bLastIsSymbol || bFormulaMode;
946
0
    if ( bDisable )
947
0
        nCntrl &= ~EEControlBits::AUTOCORRECT;
948
0
    else
949
0
        nCntrl |= EEControlBits::AUTOCORRECT;
950
951
0
    if ( nCntrl != nOld )
952
0
        mpEditEngine->SetControlWord(nCntrl);
953
0
}
954
955
void ScInputHandler::UpdateSpellSettings( bool bFromStartTab )
956
0
{
957
0
    if ( !pActiveViewSh )
958
0
        return;
959
960
0
    ScViewData& rViewData = pActiveViewSh->GetViewData();
961
0
    bool bOnlineSpell = pActiveViewSh->IsAutoSpell();
962
963
    //  SetDefaultLanguage is independent of the language attributes,
964
    //  ScGlobal::GetEditDefaultLanguage is always used.
965
    //  It must be set every time in case the office language was changed.
966
967
0
    mpEditEngine->SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() );
968
969
    //  if called for changed options, update flags only if already editing
970
    //  if called from StartTable, always update flags
971
972
0
    if ( bFromStartTab || eMode != SC_INPUT_NONE )
973
0
    {
974
0
        EEControlBits nCntrl = mpEditEngine->GetControlWord();
975
0
        EEControlBits nOld = nCntrl;
976
0
        if( bOnlineSpell )
977
0
            nCntrl |= EEControlBits::ONLINESPELLING;
978
0
        else
979
0
            nCntrl &= ~EEControlBits::ONLINESPELLING;
980
        // No AutoCorrect for Symbol Font (EditEngine does no evaluate Default)
981
0
        if ( pLastPattern && pLastPattern->IsSymbolFont() )
982
0
            nCntrl &= ~EEControlBits::AUTOCORRECT;
983
0
        else
984
0
            nCntrl |= EEControlBits::AUTOCORRECT;
985
0
        if ( nCntrl != nOld )
986
0
            mpEditEngine->SetControlWord(nCntrl);
987
988
0
        ScDocument& rDoc = rViewData.GetDocument();
989
0
        rDoc.ApplyAsianEditSettings( *mpEditEngine );
990
0
        mpEditEngine->SetDefaultHorizontalTextDirection(
991
0
            rDoc.GetEditTextDirection( rViewData.CurrentTabForData() ) );
992
0
        mpEditEngine->SetFirstWordCapitalization( false );
993
0
    }
994
995
    //  Language is set separately, so the speller is needed only if online spelling is active
996
0
    if ( bOnlineSpell ) {
997
0
        css::uno::Reference<css::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() );
998
0
        mpEditEngine->SetSpeller( xXSpellChecker1 );
999
0
    }
1000
1001
0
    bool bHyphen = pLastPattern && pLastPattern->GetItem(ATTR_HYPHENATE).GetValue();
1002
0
    if ( bHyphen ) {
1003
0
        css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
1004
0
        mpEditEngine->SetHyphenator( xXHyphenator );
1005
0
    }
1006
0
}
1007
1008
// Function/Range names etc. as Tip help
1009
1010
//  The other types are defined in ScDocument::GetFormulaEntries
1011
void ScInputHandler::GetFormulaData()
1012
0
{
1013
0
    if ( !pActiveViewSh )
1014
0
        return;
1015
1016
0
    ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
1017
1018
0
    if ( pFormulaData )
1019
0
        pFormulaData->clear();
1020
0
    else
1021
0
    {
1022
0
        pFormulaData.reset( new ScTypedCaseStrSet );
1023
0
    }
1024
1025
0
    if( pFormulaDataPara )
1026
0
        pFormulaDataPara->clear();
1027
0
    else
1028
0
        pFormulaDataPara.reset( new ScTypedCaseStrSet );
1029
1030
0
    const OUString aParenthesesReplacement( cParenthesesReplacement);
1031
0
    const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
1032
0
    const sal_uInt32 nListCount = pFuncList->GetCount();
1033
0
    const InputHandlerFunctionNames& rFunctionNames = ScGlobal::GetInputHandlerFunctionNames();
1034
0
    *pFormulaData     = rFunctionNames.maFunctionData;
1035
0
    *pFormulaDataPara = rFunctionNames.maFunctionDataPara;
1036
0
    maFormulaChar     = rFunctionNames.maFunctionChar;
1037
1038
    // Increase suggestion priority of MRU formulas
1039
0
    const ScAppOptions& rOpt = ScModule::get()->GetAppOptions();
1040
0
    const sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount();
1041
0
    const sal_uInt16* pMRUList = rOpt.GetLRUFuncList();
1042
0
    for (sal_uInt16 i = 0; i < nMRUCount; i++)
1043
0
    {
1044
0
        const sal_uInt16 nId = pMRUList[i];
1045
0
        for (sal_uInt32 j = 0; j < nListCount; j++)
1046
0
        {
1047
0
            const ScFuncDesc* pDesc = pFuncList->GetFunction(j);
1048
0
            if (pDesc->nFIndex == nId && pDesc->mxFuncName)
1049
0
            {
1050
0
                const OUString aEntry = *pDesc->mxFuncName + aParenthesesReplacement;;
1051
0
                const ScTypedStrData aData(aEntry, 0.0, 0.0, ScTypedStrData::Standard);
1052
0
                auto it = pFormulaData->find(aData);
1053
0
                if (it != pFormulaData->end())
1054
0
                    pFormulaData->erase(it);
1055
0
                pFormulaData->insert(ScTypedStrData(aEntry, 0.0, 0.0, ScTypedStrData::MRU));
1056
0
                break; // Stop searching
1057
0
            }
1058
0
        }
1059
0
    }
1060
0
    miAutoPosFormula = pFormulaData->end();
1061
1062
    // tdf#142031 - collect all the characters for the formula suggestion auto input
1063
0
    ScTypedCaseStrSet aStrSet;
1064
0
    rDoc.GetFormulaEntries( aStrSet );
1065
0
    for (auto iter = aStrSet.begin(); iter != aStrSet.end(); ++iter)
1066
0
    {
1067
0
        const OUString aFuncName = ScGlobal::getCharClass().uppercase((*iter).GetString());
1068
        // fdo#75264 fill maFormulaChar with all characters used in formula names
1069
0
        for (sal_Int32 j = 0; j < aFuncName.getLength(); j++)
1070
0
            maFormulaChar.insert(aFuncName[j]);
1071
0
    }
1072
0
    pFormulaData->insert(aStrSet.begin(), aStrSet.end());
1073
0
    pFormulaDataPara->insert(aStrSet.begin(), aStrSet.end());
1074
0
}
1075
1076
IMPL_LINK( ScInputHandler, ShowHideTipVisibleParentListener, VclWindowEvent&, rEvent, void )
1077
0
{
1078
0
    if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide
1079
0
        || rEvent.GetId() == VclEventId::WindowLoseFocus || rEvent.GetId() == VclEventId::ControlLoseFocus)
1080
0
        HideTip();
1081
0
}
1082
1083
IMPL_LINK( ScInputHandler, ShowHideTipVisibleSecParentListener, VclWindowEvent&, rEvent, void )
1084
0
{
1085
0
    if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide
1086
0
        || rEvent.GetId() == VclEventId::WindowLoseFocus || rEvent.GetId() == VclEventId::ControlLoseFocus)
1087
0
        HideTipBelow();
1088
0
}
1089
1090
void ScInputHandler::HideTip()
1091
0
{
1092
0
    if ( nTipVisible )
1093
0
    {
1094
0
        pTipVisibleParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) );
1095
0
        Help::HidePopover(pTipVisibleParent, nTipVisible );
1096
0
        nTipVisible = nullptr;
1097
0
        pTipVisibleParent = nullptr;
1098
0
    }
1099
0
    aManualTip.clear();
1100
1101
0
    const SfxViewShell* pViewShell = SfxViewShell::Current();
1102
0
    if (comphelper::LibreOfficeKit::isActive() && pViewShell)
1103
0
        pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST, "hidetip"_ostr);
1104
0
}
1105
void ScInputHandler::HideTipBelow()
1106
0
{
1107
0
    if ( nTipVisibleSec )
1108
0
    {
1109
0
        pTipVisibleSecParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) );
1110
0
        Help::HidePopover(pTipVisibleSecParent, nTipVisibleSec);
1111
0
        nTipVisibleSec = nullptr;
1112
0
        pTipVisibleSecParent = nullptr;
1113
0
    }
1114
0
    aManualTip.clear();
1115
0
}
1116
1117
namespace
1118
{
1119
1120
bool lcl_hasSingleToken(std::u16string_view s, sal_Unicode c)
1121
0
{
1122
0
    return !s.empty() && s.find(c) == std::u16string_view::npos;
1123
0
}
1124
1125
}
1126
1127
void ScInputHandler::ShowArgumentsTip( OUString& rSelText )
1128
0
{
1129
0
    if ( !pActiveViewSh )
1130
0
        return;
1131
1132
0
    ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
1133
0
    const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
1134
0
    const sal_Unicode cSheetSep = pDocSh->GetDocument().GetSheetSeparator();
1135
0
    FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
1136
0
    bool bFound = false;
1137
0
    while( !bFound )
1138
0
    {
1139
0
        rSelText += ")";
1140
0
        sal_Int32 nLeftParentPos = lcl_MatchParenthesis( rSelText, rSelText.getLength()-1 );
1141
0
        if( nLeftParentPos != -1 )
1142
0
        {
1143
0
            sal_Int32 nNextFStart = aHelper.GetFunctionStart( rSelText, nLeftParentPos, true);
1144
0
            const IFunctionDescription* ppFDesc;
1145
0
            ::std::vector< OUString> aArgs;
1146
0
            if( aHelper.GetNextFunc( rSelText, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
1147
0
            {
1148
0
                if( !ppFDesc->getFunctionName().isEmpty() )
1149
0
                {
1150
0
                    sal_Int32 nArgPos = aHelper.GetArgStart( rSelText, nNextFStart, 0 );
1151
0
                    sal_uInt16 nArgs = static_cast<sal_uInt16>(ppFDesc->getParameterCount());
1152
0
                    OUString aFuncName( ppFDesc->getFunctionName() + "(");
1153
0
                    OUString aNew;
1154
0
                    ScTypedCaseStrSet::const_iterator it =
1155
0
                        findText(*pFormulaDataPara, pFormulaDataPara->end(), aFuncName, aNew, false);
1156
0
                    if (it != pFormulaDataPara->end())
1157
0
                    {
1158
0
                        bool bFlag = false;
1159
0
                        sal_uInt16 nActive = 0;
1160
0
                        for( sal_uInt16 i=0; i < nArgs; i++ )
1161
0
                        {
1162
0
                            sal_Int32 nLength = aArgs[i].getLength();
1163
0
                            if( nArgPos <= rSelText.getLength()-1 )
1164
0
                            {
1165
0
                                nActive = i+1;
1166
0
                                bFlag = true;
1167
0
                            }
1168
0
                            nArgPos+=nLength+1;
1169
0
                        }
1170
0
                        if( bFlag )
1171
0
                        {
1172
0
                            sal_Int32 nStartPosition = 0;
1173
0
                            sal_Int32 nEndPosition = 0;
1174
1175
0
                            if( lcl_hasSingleToken(aNew, cSep) )
1176
0
                            {
1177
0
                                for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
1178
0
                                {
1179
0
                                    sal_Unicode cNext = aNew[i];
1180
0
                                    if( cNext == '(' )
1181
0
                                    {
1182
0
                                        nStartPosition = i+1;
1183
0
                                    }
1184
0
                                }
1185
0
                            }
1186
0
                            else if( lcl_hasSingleToken(aNew, cSheetSep) )
1187
0
                            {
1188
0
                                sal_uInt16 nCount = 0;
1189
0
                                for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
1190
0
                                {
1191
0
                                    sal_Unicode cNext = aNew[i];
1192
0
                                    if( cNext == '(' )
1193
0
                                    {
1194
0
                                        nStartPosition = i+1;
1195
0
                                    }
1196
0
                                    else if( cNext == cSep )
1197
0
                                    {
1198
0
                                        nCount ++;
1199
0
                                        nEndPosition = i;
1200
0
                                        if( nCount == nActive )
1201
0
                                        {
1202
0
                                            break;
1203
0
                                        }
1204
0
                                        nStartPosition = nEndPosition+1;
1205
0
                                    }
1206
0
                                }
1207
0
                            }
1208
0
                            else
1209
0
                            {
1210
0
                                sal_uInt16 nCount = 0;
1211
0
                                for (sal_Int32 i = 0; i < aNew.getLength(); ++i)
1212
0
                                {
1213
0
                                    sal_Unicode cNext = aNew[i];
1214
0
                                    if( cNext == '(' )
1215
0
                                    {
1216
0
                                        nStartPosition = i+1;
1217
0
                                    }
1218
0
                                    else if( cNext == cSep )
1219
0
                                    {
1220
0
                                        nCount ++;
1221
0
                                        nEndPosition = i;
1222
0
                                        if( nCount == nActive )
1223
0
                                        {
1224
0
                                            break;
1225
0
                                        }
1226
0
                                        nStartPosition = nEndPosition+1;
1227
0
                                    }
1228
0
                                    else if( cNext == cSheetSep )
1229
0
                                    {
1230
0
                                        continue;
1231
0
                                    }
1232
0
                                }
1233
0
                            }
1234
1235
0
                            if (nStartPosition > 0)
1236
0
                            {
1237
0
                                nArgs = ppFDesc->getParameterCount();
1238
0
                                sal_Int16 nVarArgsSet = 0;
1239
0
                                if ( nArgs >= PAIRED_VAR_ARGS )
1240
0
                                {
1241
0
                                    nVarArgsSet = 2;
1242
0
                                    nArgs -= PAIRED_VAR_ARGS - nVarArgsSet;
1243
0
                                }
1244
0
                                else if ( nArgs >= VAR_ARGS )
1245
0
                                {
1246
0
                                    nVarArgsSet = 1;
1247
0
                                    nArgs -= VAR_ARGS - nVarArgsSet;
1248
0
                                }
1249
0
                                if ( nVarArgsSet > 0 && nActive > nArgs )
1250
0
                                    nActive = nArgs - (nActive - nArgs) % nVarArgsSet;
1251
0
                                aNew = OUString::Concat(aNew.subView(0, nStartPosition)) +
1252
0
                                        u"\x25BA" +
1253
0
                                        aNew.subView(nStartPosition) +
1254
0
                                        " : " +
1255
0
                                        ppFDesc->getParameterDescription(nActive-1);
1256
0
                                if (eMode != SC_INPUT_TOP)
1257
0
                                {
1258
0
                                    ShowTipBelow( aNew );
1259
0
                                }
1260
0
                                else
1261
0
                                {
1262
0
                                    ShowTip(aNew);
1263
0
                                }
1264
0
                                bFound = true;
1265
0
                            }
1266
0
                        }
1267
0
                        else
1268
0
                        {
1269
0
                            ShowTipBelow( aNew );
1270
0
                            bFound = true;
1271
0
                        }
1272
1273
0
                        const SfxViewShell* pViewShell = SfxViewShell::Current();
1274
0
                        if (comphelper::LibreOfficeKit::isActive() && pViewShell && pViewShell->isLOKDesktop())
1275
0
                        {
1276
0
                            tools::JsonWriter writer;
1277
0
                            writer.put("type", "formulausage");
1278
0
                            writer.put("text", aNew);
1279
0
                            OString sFunctionUsageTip = writer.finishAndGetAsOString();
1280
0
                            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TOOLTIP, sFunctionUsageTip);
1281
0
                        }
1282
0
                    }
1283
0
                }
1284
0
            }
1285
0
        }
1286
0
        else
1287
0
        {
1288
0
            break;
1289
0
        }
1290
0
    }
1291
0
}
1292
1293
void ScInputHandler::ShowTipCursor()
1294
0
{
1295
0
    HideTip();
1296
0
    HideTipBelow();
1297
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
1298
1299
    /* TODO: MLFORMULA: this should work also with multi-line formulas. */
1300
0
    if ( !(bFormulaMode && pActiveView && pFormulaDataPara && mpEditEngine->GetParagraphCount() == 1) )
1301
0
        return;
1302
1303
0
    OUString aParagraph = mpEditEngine->GetText( 0 );
1304
0
    ESelection aSel = pActiveView->GetSelection();
1305
0
    aSel.Adjust();
1306
1307
0
    if (aParagraph.getLength() < aSel.end.nIndex)
1308
0
        return;
1309
1310
0
    if (aSel.end.nIndex > 0)
1311
0
    {
1312
0
        OUString aSelText(aParagraph.copy(0, aSel.end.nIndex));
1313
1314
0
        ShowArgumentsTip( aSelText );
1315
0
    }
1316
0
}
1317
1318
void ScInputHandler::ShowTip( const OUString& rText )
1319
0
{
1320
    // aManualTip needs to be set afterwards from outside
1321
1322
0
    HideTip();
1323
0
    HideTipBelow();
1324
1325
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
1326
0
    if (!pActiveView)
1327
0
        return;
1328
1329
0
    Point aPos;
1330
0
    if (pInputWin && pInputWin->GetEditView() == pActiveView)
1331
0
    {
1332
0
        pTipVisibleParent = pInputWin->GetEditWindow();
1333
0
        aPos = pInputWin->GetCursorScreenPixelPos();
1334
0
    }
1335
0
    else
1336
0
    {
1337
0
        pTipVisibleParent = pActiveView->GetWindow();
1338
0
        if (vcl::Cursor* pCur = pActiveView->GetCursor())
1339
0
            aPos = pTipVisibleParent->LogicToPixel( pCur->GetPos() );
1340
0
        aPos = pTipVisibleParent->OutputToScreenPixel( aPos );
1341
0
    }
1342
1343
0
    tools::Rectangle aRect( aPos, aPos );
1344
0
    QuickHelpFlags const nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom;
1345
0
    nTipVisible = Help::ShowPopover(pTipVisibleParent, aRect, rText, nAlign);
1346
0
    pTipVisibleParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) );
1347
0
}
1348
1349
void ScInputHandler::ShowTipBelow( const OUString& rText )
1350
0
{
1351
0
    HideTipBelow();
1352
1353
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
1354
0
    if ( !pActiveView )
1355
0
        return;
1356
1357
0
    Point aPos;
1358
0
    if (pInputWin && pInputWin->GetEditView() == pActiveView)
1359
0
    {
1360
0
        pTipVisibleSecParent = pInputWin->GetEditWindow();
1361
0
        aPos = pInputWin->GetCursorScreenPixelPos(true);
1362
0
    }
1363
0
    else
1364
0
    {
1365
0
        pTipVisibleSecParent = pActiveView->GetWindow();
1366
0
        if (vcl::Cursor* pCur = pActiveView->GetCursor())
1367
0
        {
1368
0
            Point aLogicPos = pCur->GetPos();
1369
0
            aLogicPos.AdjustY(pCur->GetHeight() );
1370
0
            aPos = pTipVisibleSecParent->LogicToPixel( aLogicPos );
1371
0
        }
1372
0
        aPos = pTipVisibleSecParent->OutputToScreenPixel( aPos );
1373
0
    }
1374
1375
0
    tools::Rectangle aRect( aPos, aPos );
1376
0
    QuickHelpFlags const nAlign = QuickHelpFlags::Left | QuickHelpFlags::Top | QuickHelpFlags::NoEvadePointer;
1377
0
    nTipVisibleSec = Help::ShowPopover(pTipVisibleSecParent, aRect, rText, nAlign);
1378
0
    pTipVisibleSecParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) );
1379
0
}
1380
1381
bool ScInputHandler::GetFuncName( OUString& aStart, OUString& aResult )
1382
0
{
1383
0
    if ( aStart.isEmpty() )
1384
0
        return false;
1385
1386
0
    aStart = ScGlobal::getCharClass().uppercase( aStart );
1387
0
    sal_Int32 nPos = aStart.getLength() - 1;
1388
0
    sal_Unicode c = aStart[ nPos ];
1389
    // fdo#75264 use maFormulaChar to check if characters are used in function names
1390
0
    ::std::set< sal_Unicode >::const_iterator p = maFormulaChar.find( c );
1391
0
    if ( p == maFormulaChar.end() )
1392
0
        return false; // last character is not part of any function name, quit
1393
1394
0
    ::std::vector<sal_Unicode> aTemp { c };
1395
0
    for(sal_Int32 i = nPos - 1; i >= 0; --i)
1396
0
    {
1397
0
        c = aStart[ i ];
1398
0
        p = maFormulaChar.find( c );
1399
1400
0
        if (p == maFormulaChar.end())
1401
0
            break;
1402
1403
0
        aTemp.push_back( c );
1404
0
    }
1405
1406
0
    ::std::vector<sal_Unicode>::reverse_iterator rIt = aTemp.rbegin();
1407
0
    aResult = OUString( *rIt++ );
1408
0
    while ( rIt != aTemp.rend() )
1409
0
        aResult += OUStringChar( *rIt++ );
1410
1411
0
    return true;
1412
0
}
1413
1414
namespace {
1415
    /// Rid ourselves of unwanted " quoted json characters.
1416
    OString escapeJSON(const OUString &aStr)
1417
0
    {
1418
0
        OUString aEscaped = aStr;
1419
0
        aEscaped = aEscaped.replaceAll("\n", " ");
1420
0
        aEscaped = aEscaped.replaceAll("\"", "'");
1421
0
        return OUStringToOString(aEscaped, RTL_TEXTENCODING_UTF8);
1422
0
    }
1423
}
1424
1425
void ScInputHandler::ShowFuncList( const ::std::vector< OUString > & rFuncStrVec )
1426
0
{
1427
0
    const SfxViewShell* pViewShell = SfxViewShell::Current();
1428
0
    if (comphelper::LibreOfficeKit::isActive())
1429
0
    {
1430
0
        if (rFuncStrVec.size() && pViewShell)
1431
0
        {
1432
0
            auto aPos = pFormulaData->begin();
1433
0
            sal_uInt32 nCurIndex = std::distance(aPos, miAutoPosFormula);
1434
0
            const sal_uInt32 nSize = pFormulaData->size();
1435
1436
0
            OUString aFuncNameStr;
1437
0
            OUString aDescFuncNameStr;
1438
0
            OStringBuffer aPayload("[ ");
1439
0
            for (const OUString& rFunc : rFuncStrVec)
1440
0
            {
1441
0
                if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement )
1442
0
                {
1443
0
                    aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1);
1444
0
                }
1445
0
                else
1446
0
                {
1447
0
                    aFuncNameStr = rFunc;
1448
0
                }
1449
1450
0
                FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
1451
0
                aDescFuncNameStr = aFuncNameStr + "()";
1452
0
                sal_Int32 nNextFStart = 0;
1453
0
                const IFunctionDescription* ppFDesc;
1454
0
                ::std::vector< OUString > aArgs;
1455
0
                OUString eqPlusFuncName = "=" + aDescFuncNameStr;
1456
0
                if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
1457
0
                {
1458
0
                    if ( !ppFDesc->getFunctionName().isEmpty() )
1459
0
                    {
1460
0
                        aPayload.append("{"
1461
0
                            "\"index\": "
1462
0
                            + OString::number(static_cast<sal_Int64>(nCurIndex))
1463
0
                            + ", "
1464
0
                            "\"signature\": \""
1465
0
                            + escapeJSON(ppFDesc->getSignature())
1466
0
                            + "\", "
1467
0
                            "\"description\": \""
1468
0
                            + escapeJSON(ppFDesc->getDescription())
1469
0
                            + "\", \"namedRange\": false }, ");
1470
0
                    }
1471
0
                    else
1472
0
                    {
1473
0
                        aPayload.append("{"
1474
0
                            "\"index\": "
1475
0
                            + OString::number(static_cast<sal_Int64>(nCurIndex))
1476
0
                            + ", "
1477
0
                                "\"signature\": \""
1478
0
                            + escapeJSON(aFuncNameStr)
1479
0
                            + "\", "
1480
0
                                "\"description\": \""
1481
0
                            + escapeJSON(OUString())
1482
0
                            + "\", \"namedRange\": true }, ");
1483
0
                    }
1484
0
                }
1485
0
                ++nCurIndex;
1486
0
                if (nCurIndex == nSize)
1487
0
                    nCurIndex = 0;
1488
0
            }
1489
0
            sal_Int32 nLen = aPayload.getLength();
1490
0
            if (nLen <= 2)
1491
0
            {
1492
0
                aPayload[nLen - 1] = ']';
1493
0
            }
1494
0
            else
1495
0
            {
1496
0
                aPayload[nLen - 2] = ' ';
1497
0
                aPayload[nLen - 1] = ']';
1498
0
            }
1499
1500
0
            OString s = aPayload.makeStringAndClear();
1501
0
            pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST, s);
1502
0
        }
1503
0
        return;
1504
0
    }
1505
1506
0
    OUStringBuffer aTipStr;
1507
0
    OUString aFuncNameStr;
1508
0
    OUString aDescFuncNameStr;
1509
0
    ::std::vector<OUString>::const_iterator itStr = rFuncStrVec.begin();
1510
0
    sal_Int32 nMaxFindNumber = 3;
1511
0
    sal_Int32 nRemainFindNumber = nMaxFindNumber;
1512
0
    for ( ; itStr != rFuncStrVec.end(); ++itStr )
1513
0
    {
1514
0
        const OUString& rFunc = *itStr;
1515
0
        if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement )
1516
0
        {
1517
0
            aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1);
1518
0
        }
1519
0
        else
1520
0
        {
1521
0
            aFuncNameStr = rFunc;
1522
0
        }
1523
0
        if ( itStr == rFuncStrVec.begin() )
1524
0
        {
1525
0
            aTipStr = "[";
1526
0
            aDescFuncNameStr = aFuncNameStr + "()";
1527
0
        }
1528
0
        else
1529
0
        {
1530
0
            aTipStr.append(", ");
1531
0
        }
1532
0
        aTipStr.append(aFuncNameStr);
1533
0
        if ( itStr == rFuncStrVec.begin() )
1534
0
            aTipStr.append("]");
1535
0
        if ( --nRemainFindNumber <= 0 )
1536
0
            break;
1537
0
    }
1538
0
    sal_Int32 nRemainNumber = rFuncStrVec.size() - nMaxFindNumber;
1539
0
    if ( nRemainFindNumber == 0 && nRemainNumber > 0 )
1540
0
    {
1541
0
        OUString aMessage( ScResId( STR_FUNCTIONS_FOUND ) );
1542
0
        aMessage = aMessage.replaceFirst("%2", OUString::number(nRemainNumber));
1543
0
        aMessage = aMessage.replaceFirst("%1", aTipStr);
1544
0
        aTipStr = aMessage;
1545
0
    }
1546
0
    FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr());
1547
0
    sal_Int32 nNextFStart = 0;
1548
0
    const IFunctionDescription* ppFDesc;
1549
0
    ::std::vector< OUString > aArgs;
1550
0
    OUString eqPlusFuncName = "=" + aDescFuncNameStr;
1551
0
    if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) )
1552
0
    {
1553
0
        if ( !ppFDesc->getFunctionName().isEmpty() )
1554
0
        {
1555
0
            aTipStr.append(" : " + ppFDesc->getDescription());
1556
0
        }
1557
0
    }
1558
0
    ShowTip( aTipStr.makeStringAndClear() );
1559
0
}
1560
1561
void ScInputHandler::UseFormulaData()
1562
0
{
1563
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
1564
1565
    /* TODO: MLFORMULA: this should work also with multi-line formulas. */
1566
0
    if ( !(pActiveView && pFormulaData && mpEditEngine->GetParagraphCount() == 1) )
1567
0
        return;
1568
1569
0
    OUString aParagraph = mpEditEngine->GetText( 0 );
1570
0
    ESelection aSel = pActiveView->GetSelection();
1571
0
    aSel.Adjust();
1572
1573
    // Due to differences between table and input cell (e.g clipboard with line breaks),
1574
    // the selection may not be in line with the EditEngine anymore.
1575
    // Just return without any indication as to why.
1576
0
    if (aSel.end.nIndex > aParagraph.getLength())
1577
0
        return;
1578
1579
0
    if ( aParagraph.getLength() > aSel.end.nIndex &&
1580
0
         ( ScGlobal::getCharClass().isLetterNumeric( aParagraph, aSel.end.nIndex ) ||
1581
0
           aParagraph[ aSel.end.nIndex ] == '_' ||
1582
0
           aParagraph[ aSel.end.nIndex ] == '.' ||
1583
0
           aParagraph[ aSel.end.nIndex ] == '$'   ) )
1584
0
        return;
1585
1586
    //  Is the cursor at the end of a word?
1587
0
    if (aSel.end.nIndex <= 0)
1588
0
        return;
1589
1590
0
    OUString aSelText(aParagraph.copy(0, aSel.end.nIndex));
1591
1592
0
    OUString aText;
1593
0
    if ( GetFuncName( aSelText, aText ) )
1594
0
    {
1595
        // function name is incomplete:
1596
        // show matching functions name as tip above cell
1597
0
        ::std::vector<OUString> aNewVec;
1598
0
        miAutoPosFormula = pFormulaData->end();
1599
0
        miAutoPosFormula = findTextAll(*pFormulaData, miAutoPosFormula, aText, aNewVec, false);
1600
0
        if (miAutoPosFormula != pFormulaData->end())
1601
0
        {
1602
            // check if partial function name is not between quotes
1603
0
            sal_Unicode cBetweenQuotes = 0;
1604
0
            for ( int n = 0; n < aSelText.getLength(); n++ )
1605
0
            {
1606
0
                if (cBetweenQuotes)
1607
0
                {
1608
0
                    if (aSelText[n] == cBetweenQuotes)
1609
0
                        cBetweenQuotes = 0;
1610
0
                }
1611
0
                else if ( aSelText[ n ] == '"' )
1612
0
                    cBetweenQuotes = '"';
1613
0
                else if ( aSelText[ n ] == '\'' )
1614
0
                    cBetweenQuotes = '\'';
1615
0
            }
1616
0
            if ( cBetweenQuotes )
1617
0
                return;  // we're between quotes
1618
1619
0
            ShowFuncList(aNewVec);
1620
0
            aAutoSearch = aText;
1621
0
        }
1622
0
        return;
1623
0
    }
1624
1625
    // function name is complete:
1626
    // show tip below the cell with function name and arguments of function
1627
0
    ShowArgumentsTip( aSelText );
1628
0
}
1629
1630
void ScInputHandler::NextFormulaEntry( bool bBack )
1631
0
{
1632
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
1633
0
    if ( pActiveView && pFormulaData )
1634
0
    {
1635
0
        ::std::vector<OUString> aNewVec;
1636
0
        ScTypedCaseStrSet::const_iterator itNew = findTextAll(*pFormulaData, miAutoPosFormula, aAutoSearch, aNewVec, bBack);
1637
0
        if (itNew != pFormulaData->end())
1638
0
        {
1639
0
            miAutoPosFormula = itNew;
1640
0
            ShowFuncList( aNewVec );
1641
0
        }
1642
0
    }
1643
1644
    // For Tab we always call HideCursor first
1645
0
    if (pActiveView)
1646
0
        pActiveView->ShowCursor();
1647
0
}
1648
1649
namespace {
1650
1651
void completeFunction( EditView* pView, const OUString& rInsert, bool& rParInserted )
1652
0
{
1653
0
    if (!pView)
1654
0
        return;
1655
1656
0
    ESelection aSel = pView->GetSelection();
1657
1658
0
    bool bNoInitialLetter = false;
1659
0
    OUString aOld = pView->getEditEngine().GetText(0);
1660
    // in case we want just insert a function and not completing
1661
0
    if ( comphelper::LibreOfficeKit::isActive() )
1662
0
    {
1663
0
        ESelection aSelRange = aSel;
1664
0
        --aSelRange.start.nIndex;
1665
0
        --aSelRange.end.nIndex;
1666
0
        pView->SetSelection(aSelRange);
1667
0
        pView->SelectCurrentWord();
1668
1669
0
        if ( aOld == "=" )
1670
0
        {
1671
0
            bNoInitialLetter = true;
1672
0
            aSelRange.start.nIndex = 1;
1673
0
            aSelRange.end.nIndex = 1;
1674
0
            pView->SetSelection(aSelRange);
1675
0
        }
1676
0
        else if ( pView->GetSelected().startsWith("()") )
1677
0
        {
1678
0
            bNoInitialLetter = true;
1679
0
            ++aSelRange.start.nIndex;
1680
0
            ++aSelRange.end.nIndex;
1681
0
            pView->SetSelection(aSelRange);
1682
0
        }
1683
0
    }
1684
1685
0
    if(!bNoInitialLetter)
1686
0
    {
1687
0
        const sal_Int32 nMinLen = std::max(aSel.end.nIndex - aSel.start.nIndex, sal_Int32(1));
1688
        // Since transliteration service is used to test for match, the replaced string could be
1689
        // longer than rInsert, so in order to find longest match before the cursor, test whole
1690
        // string from start to current cursor position (don't limit to length of rInsert)
1691
        // Disclaimer: I really don't know if a match longer than rInsert is actually possible,
1692
        // so the above is based on assumptions how "transliteration" might possibly work. If
1693
        // it's in fact impossible, an optimization would be useful to limit aSel.start.nPos to
1694
        // std::max(sal_Int32(0), aSel.end.nIndex - rInsert.getLength()).
1695
0
        aSel.start.nIndex = 0;
1696
0
        pView->SetSelection(aSel);
1697
0
        const OUString aAll = pView->GetSelected();
1698
0
        OUString aMatch;
1699
0
        for (sal_Int32 n = aAll.getLength(); n >= nMinLen && aMatch.isEmpty(); --n)
1700
0
        {
1701
0
            const OUString aTest = aAll.copy(aAll.getLength() - n); // n trailing chars
1702
0
            if (ScGlobal::GetTransliteration().isMatch(aTest, rInsert))
1703
0
                aMatch = aTest; // Found => break the loop
1704
0
        }
1705
1706
0
        aSel.start.nIndex = aSel.end.nIndex - aMatch.getLength();
1707
0
        pView->SetSelection(aSel);
1708
0
    }
1709
1710
0
    OUString aInsStr = rInsert;
1711
0
    sal_Int32 nInsLen = aInsStr.getLength();
1712
0
    bool bDoParen = ( nInsLen > 1 && aInsStr[nInsLen-2] == '('
1713
0
                                  && aInsStr[nInsLen-1] == ')' );
1714
0
    if ( bDoParen )
1715
0
    {
1716
        // Do not insert parentheses after function names if there already are some
1717
        // (e.g. if the function name was edited).
1718
0
        ESelection aWordSel = pView->GetSelection();
1719
1720
        // aWordSel.EndPos points one behind string if word at end
1721
0
        if (aWordSel.end.nIndex < aOld.getLength())
1722
0
        {
1723
0
            sal_Unicode cNext = aOld[aWordSel.end.nIndex];
1724
0
            if ( cNext == '(' )
1725
0
            {
1726
0
                bDoParen = false;
1727
0
                aInsStr = aInsStr.copy( 0, nInsLen - 2 ); // Skip parentheses
1728
0
            }
1729
0
        }
1730
0
    }
1731
1732
0
    pView->InsertText( aInsStr );
1733
1734
0
    if ( bDoParen ) // Put cursor between parentheses
1735
0
    {
1736
0
        aSel = pView->GetSelection();
1737
0
        --aSel.start.nIndex;
1738
0
        --aSel.end.nIndex;
1739
0
        pView->SetSelection(aSel);
1740
1741
0
        rParInserted = true;
1742
0
    }
1743
0
}
1744
1745
}
1746
1747
void ScInputHandler::PasteFunctionData()
1748
0
{
1749
0
    if (pFormulaData && miAutoPosFormula != pFormulaData->end())
1750
0
    {
1751
0
        const ScTypedStrData& rData = *miAutoPosFormula;
1752
0
        OUString aInsert = rData.GetString();
1753
0
        if (aInsert[aInsert.getLength()-1] == cParenthesesReplacement)
1754
0
            aInsert = OUString::Concat(aInsert.subView( 0, aInsert.getLength()-1)) + "()";
1755
0
        bool bParInserted = false;
1756
1757
0
        DataChanging(); // Cannot be new
1758
0
        completeFunction( pTopView, aInsert, bParInserted );
1759
0
        completeFunction( pTableView, aInsert, bParInserted );
1760
0
        DataChanged();
1761
0
        ShowTipCursor();
1762
1763
0
        if (bParInserted)
1764
0
            AutoParAdded();
1765
0
    }
1766
1767
0
    HideTip();
1768
1769
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
1770
0
    if (comphelper::LibreOfficeKit::isActive() && pTopView && pInputWin)
1771
0
        pInputWin->TextGrabFocus();
1772
0
    if (pActiveView)
1773
0
        pActiveView->ShowCursor();
1774
0
}
1775
1776
void ScInputHandler::LOKPasteFunctionData(const OUString& rFunctionName)
1777
0
{
1778
    // in case we have no top view try to create it
1779
0
    if (!pTopView && pInputWin)
1780
0
    {
1781
0
        ScInputMode eCurMode = eMode;
1782
0
        SetMode(SC_INPUT_TOP);
1783
0
        if (!pTopView)
1784
0
            SetMode(eCurMode);
1785
0
    }
1786
1787
0
    EditView* pEditView = pTopView ? pTopView : pTableView;
1788
1789
0
    if (!pActiveViewSh || !pEditView)
1790
0
        return;
1791
1792
0
    bool bEdit = false;
1793
0
    OUString aFormula;
1794
0
    EditEngine const& rEditEngine = pEditView->getEditEngine();
1795
0
    {
1796
0
        aFormula = rEditEngine.GetText(0);
1797
        /* TODO: LOK: are you sure you want '+' and '-' let start formulas with
1798
         * function names? That was meant for "data typist" numeric keyboard
1799
         * input. */
1800
0
        bEdit = aFormula.getLength() > 1 && (aFormula[0] == '=' || aFormula[0] == '+' || aFormula[0] == '-');
1801
0
    }
1802
1803
0
    if ( !bEdit )
1804
0
    {
1805
0
        OUString aNewFormula('=');
1806
0
        if ( aFormula.startsWith("=") )
1807
0
            aNewFormula = aFormula;
1808
1809
0
        InputReplaceSelection( aNewFormula );
1810
0
    }
1811
1812
0
    if (pFormulaData)
1813
0
    {
1814
0
        OUString aNew;
1815
0
        ScTypedCaseStrSet::const_iterator aPos = findText(*pFormulaData, pFormulaData->begin(), rFunctionName, aNew, /* backward = */false);
1816
1817
0
        if (aPos != pFormulaData->end())
1818
0
        {
1819
0
            miAutoPosFormula = aPos;
1820
0
            PasteFunctionData();
1821
0
        }
1822
0
    }
1823
0
}
1824
1825
void ScTabViewShell::LOKSendFormulabarUpdate(const EditView* pActiveView,
1826
                                             const OUString& rText,
1827
                                             const ESelection& rSelection)
1828
0
{
1829
0
    OUString aSelection;
1830
0
    if (pActiveView)
1831
0
    {
1832
0
        aSelection = OUString::number(pActiveView->GetPosWithField(0, rSelection.start.nIndex)) + ";" +
1833
0
            OUString::number(pActiveView->GetPosWithField(0, rSelection.end.nIndex)) + ";" +
1834
0
            OUString::number(rSelection.start.nPara) + ";" + OUString::number(rSelection.end.nPara);
1835
0
    }
1836
0
    else
1837
0
    {
1838
0
        aSelection = OUString::number(rSelection.start.nIndex) + ";" + OUString::number(rSelection.end.nIndex) + ";" +
1839
0
            OUString::number(rSelection.start.nPara) + ";" + OUString::number(rSelection.end.nPara);
1840
0
    }
1841
1842
0
    sal_uInt64 nCurrentShellId = reinterpret_cast<sal_uInt64>(this);
1843
1844
    // We can get three updates per keystroke, StartExtTextInput, ExtTextInput and PostExtTextInput
1845
    // Skip duplicate updates. Be conservative and don't skip duplicates that are 5+ seconds
1846
    // apart.
1847
0
    std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
1848
0
    if (maSendFormulabarUpdate.m_nShellId == nCurrentShellId &&
1849
0
        maSendFormulabarUpdate.m_aText == rText &&
1850
0
        maSendFormulabarUpdate.m_aSelection == aSelection &&
1851
0
        std::chrono::duration_cast<std::chrono::seconds>(
1852
0
            now - maSendFormulabarUpdate.m_nTimeStamp) < std::chrono::seconds(5))
1853
0
    {
1854
0
        return;
1855
0
    }
1856
1857
0
    maSendFormulabarUpdate.m_nShellId = nCurrentShellId;
1858
0
    maSendFormulabarUpdate.m_aText = rText;
1859
0
    maSendFormulabarUpdate.m_aSelection = aSelection;
1860
0
    maSendFormulabarUpdate.m_nTimeStamp = now;
1861
1862
0
    ScViewData& rViewData = this->GetViewData();
1863
0
    const ScDocument& rDoc = rViewData.GetDocShell()->GetDocument();
1864
0
    const ScPatternAttr* pPattern = rDoc.GetPattern(rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetRefTabNo());
1865
1866
0
    if (pPattern)
1867
0
    {
1868
0
        SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
1869
0
        sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
1870
0
        maSendFormulabarUpdate.m_separator = pFormatter->GetFormatDecimalSep(nFormat);
1871
0
    }
1872
1873
0
    maSendFormulabarUpdate.Send();
1874
0
}
1875
1876
void ScTabViewShell::SendFormulabarUpdate::Send()
1877
0
{
1878
0
    std::unique_ptr<jsdialog::ActionDataMap> pData = std::make_unique<jsdialog::ActionDataMap>();
1879
0
    (*pData)["action_type"_ostr] = "setText";
1880
0
    (*pData)["text"_ostr] = m_aText;
1881
0
    (*pData)["selection"_ostr] = m_aSelection;
1882
0
    (*pData)["separator"_ostr] = m_separator;
1883
0
    OUString sWindowId = OUString::number(m_nShellId) + "formulabar";
1884
0
    jsdialog::SendAction(sWindowId, u"sc_input_window"_ustr, std::move(pData));
1885
0
}
1886
1887
// Calculate selection and display as tip help
1888
static OUString lcl_Calculate( const OUString& rFormula, ScDocument& rDoc, const ScAddress &rPos )
1889
0
{
1890
    //TODO: Merge with ScFormulaDlg::CalcValue and move into Document!
1891
    // Quotation marks for Strings are only inserted here.
1892
1893
0
    if(rFormula.isEmpty())
1894
0
        return OUString();
1895
1896
0
    std::optional<ScSimpleFormulaCalculator> pCalc( std::in_place, rDoc, rPos, rFormula, false );
1897
1898
    // FIXME: HACK! In order to not get a #REF! for ColRowNames, if a name is actually inserted as a Range
1899
    // into the whole Formula, but is interpreted as a single cell reference when displaying it on its own
1900
0
    bool bColRowName = pCalc->HasColRowName();
1901
0
    if ( bColRowName )
1902
0
    {
1903
        // ColRowName in RPN code?
1904
0
        if ( pCalc->GetCode()->GetCodeLen() <= 1 )
1905
0
        {   // ==1: Single one is as a Parameter always a Range
1906
            // ==0: It might be one, if ...
1907
0
            OUString aBraced = "(" + rFormula + ")";
1908
0
            pCalc.emplace( rDoc, rPos, aBraced, false );
1909
0
        }
1910
0
        else
1911
0
            bColRowName = false;
1912
0
    }
1913
1914
0
    FormulaError nErrCode = pCalc->GetErrCode();
1915
0
    if ( nErrCode != FormulaError::NONE )
1916
0
        return ScGlobal::GetErrorString(nErrCode);
1917
1918
0
    SvNumberFormatter& aFormatter = *rDoc.GetFormatTable();
1919
0
    OUString aValue;
1920
0
    if ( pCalc->IsValue() )
1921
0
    {
1922
0
        double n = pCalc->GetValue();
1923
0
        sal_uInt32 nFormat = aFormatter.GetStandardFormat( n, 0,
1924
0
                pCalc->GetFormatType(), ScGlobal::eLnge );
1925
0
        aValue = aFormatter.GetInputLineString( n, nFormat );
1926
        //! display OutputString but insert InputLineString
1927
0
    }
1928
0
    else
1929
0
    {
1930
0
        OUString aStr = pCalc->GetString().getString();
1931
0
        sal_uInt32 nFormat = aFormatter.GetStandardFormat(
1932
0
                pCalc->GetFormatType(), ScGlobal::eLnge);
1933
0
        {
1934
0
            const Color* pColor;
1935
0
            aFormatter.GetOutputString( aStr, nFormat,
1936
0
                    aValue, &pColor );
1937
0
        }
1938
1939
0
        aValue = "\"" + aValue + "\"";
1940
        //! Escape quotation marks in String??
1941
0
    }
1942
1943
0
    ScRange aTestRange;
1944
0
    if ( bColRowName || (aTestRange.Parse(rFormula, rDoc) & ScRefFlags::VALID) )
1945
0
        aValue += " ...";
1946
1947
0
    return aValue;
1948
0
}
1949
1950
void ScInputHandler::FormulaPreview()
1951
0
{
1952
0
    OUString aValue;
1953
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
1954
0
    if ( pActiveView && pActiveViewSh )
1955
0
    {
1956
0
        OUString aPart = pActiveView->GetSelected();
1957
0
        if (aPart.isEmpty())
1958
0
            aPart = mpEditEngine->GetText(0);
1959
0
        ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
1960
0
        aValue = lcl_Calculate( aPart, rDoc, aCursorPos );
1961
0
    }
1962
1963
0
    if (!aValue.isEmpty())
1964
0
    {
1965
0
        ShowTip( aValue );          //  Display as QuickHelp
1966
0
        aManualTip = aValue;        //  Set after ShowTip
1967
0
        if (pFormulaData)
1968
0
            miAutoPosFormula = pFormulaData->end();
1969
0
        if (pColumnData)
1970
0
            miAutoPosColumn = pColumnData->end();
1971
0
    }
1972
0
}
1973
1974
void ScInputHandler::PasteManualTip()
1975
0
{
1976
    //  Three dots at the end -> Range reference -> do not insert
1977
    //  FIXME: Once we have matrix constants, we can change this
1978
0
    sal_Int32 nTipLen = aManualTip.getLength();
1979
0
    sal_uInt32 const nTipLen2(sal::static_int_cast<sal_uInt32>(nTipLen));
1980
0
    if ( nTipLen && ( nTipLen < 3 || aManualTip.subView( nTipLen2-3 ) != u"..." ) )
1981
0
    {
1982
0
        DataChanging(); // Cannot be new
1983
1984
0
        OUString aInsert = aManualTip;
1985
0
        EditView* pActiveView = pTopView ? pTopView : pTableView;
1986
0
        if (!pActiveView->HasSelection())
1987
0
        {
1988
            // Nothing selected -> select everything
1989
0
            sal_Int32 nOldLen = mpEditEngine->GetTextLen(0);
1990
0
            ESelection aAllSel( 0, 0, 0, nOldLen );
1991
0
            if ( pTopView )
1992
0
                pTopView->SetSelection( aAllSel );
1993
0
            if ( pTableView )
1994
0
                pTableView->SetSelection( aAllSel );
1995
0
        }
1996
1997
0
        ESelection aSel = pActiveView->GetSelection();
1998
0
        aSel.Adjust();
1999
0
        OSL_ENSURE( !aSel.start.nPara && !aSel.end.nPara, "Too many paragraphs in Formula" );
2000
0
        if ( !aSel.start.nIndex )  // Selection from the start?
2001
0
        {
2002
0
            if ( aSel.end.nIndex == mpEditEngine->GetTextLen(0) )
2003
0
            {
2004
                //  Everything selected -> skip quotation marks
2005
0
                if ( aInsert[0] == '"' )
2006
0
                    aInsert = aInsert.copy(1);
2007
0
                sal_Int32 nInsLen = aInsert.getLength();
2008
0
                if ( aInsert.endsWith("\"") )
2009
0
                    aInsert = aInsert.copy( 0, nInsLen-1 );
2010
0
            }
2011
0
            else if ( aSel.end.nIndex )
2012
0
            {
2013
                //  Not everything selected -> do not overwrite equality sign
2014
                //FIXME: Even double equality signs??
2015
0
                aSel.start.nIndex = 1;
2016
0
                if ( pTopView )
2017
0
                    pTopView->SetSelection( aSel );
2018
0
                if ( pTableView )
2019
0
                    pTableView->SetSelection( aSel );
2020
0
            }
2021
0
        }
2022
0
        if ( pTopView )
2023
0
            pTopView->InsertText( aInsert, true );
2024
0
        if ( pTableView )
2025
0
            pTableView->InsertText( aInsert, true );
2026
2027
0
        DataChanged();
2028
0
    }
2029
2030
0
    HideTip();
2031
0
}
2032
2033
void ScInputHandler::ResetAutoPar()
2034
0
{
2035
0
    nAutoPar = 0;
2036
0
}
2037
2038
void ScInputHandler::AutoParAdded()
2039
0
{
2040
0
    ++nAutoPar; // Closing parenthesis can be overwritten
2041
0
}
2042
2043
bool ScInputHandler::CursorAtClosingPar()
2044
0
{
2045
    // Test if the cursor is before a closing parenthesis
2046
    // Selection from SetReference has been removed before
2047
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
2048
0
    if ( pActiveView && !pActiveView->HasSelection() && bFormulaMode )
2049
0
    {
2050
0
        ESelection aSel = pActiveView->GetSelection();
2051
0
        sal_Int32 nPos = aSel.start.nIndex;
2052
0
        OUString aFormula = mpEditEngine->GetText(0);
2053
0
        if ( nPos < aFormula.getLength() && aFormula[nPos] == ')' )
2054
0
            return true;
2055
0
    }
2056
0
    return false;
2057
0
}
2058
2059
void ScInputHandler::SkipClosingPar()
2060
0
{
2061
    //  this is called when a ')' is typed and the cursor is before a ')'
2062
    //  that can be overwritten -> just set the cursor behind the ')'
2063
2064
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
2065
0
    if (pActiveView)
2066
0
    {
2067
0
        ESelection aSel = pActiveView->GetSelection();
2068
0
        ++aSel.start.nIndex;
2069
0
        ++aSel.end.nIndex;
2070
2071
        //  this is in a formula (only one paragraph), so the selection
2072
        //  can be used directly for the TopView
2073
2074
0
        if ( pTopView )
2075
0
            pTopView->SetSelection( aSel );
2076
0
        if ( pTableView )
2077
0
            pTableView->SetSelection( aSel );
2078
0
    }
2079
2080
0
    OSL_ENSURE(nAutoPar, "SkipClosingPar: count is wrong");
2081
0
    --nAutoPar;
2082
0
}
2083
2084
// Auto input
2085
2086
void ScInputHandler::GetColData()
2087
0
{
2088
0
    if ( !pActiveViewSh )
2089
0
        return;
2090
2091
0
    ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
2092
2093
0
    if ( pColumnData )
2094
0
        pColumnData->clear();
2095
0
    else
2096
0
        pColumnData.reset( new ScTypedCaseStrSet );
2097
2098
0
    std::vector<ScTypedStrData> aEntries;
2099
0
    rDoc.GetDataEntries(
2100
0
        aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), aEntries);
2101
0
    if (!aEntries.empty())
2102
0
        pColumnData->insert(aEntries.begin(), aEntries.end());
2103
2104
0
    miAutoPosColumn = pColumnData->end();
2105
0
}
2106
2107
void ScInputHandler::UseColData() // When typing
2108
0
{
2109
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
2110
0
    if ( !(pActiveView && pColumnData) )
2111
0
        return;
2112
2113
    //  Only change when cursor is at the end
2114
0
    ESelection aSel = pActiveView->GetSelection();
2115
0
    aSel.Adjust();
2116
2117
0
    sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
2118
0
    if (aSel.end.nPara + 1 != nParCnt)
2119
0
        return;
2120
2121
0
    sal_Int32 nParLen = mpEditEngine->GetTextLen(aSel.end.nPara);
2122
0
    if (aSel.end.nIndex != nParLen)
2123
0
        return;
2124
2125
0
    OUString aText = GetEditText(mpEditEngine.get());
2126
0
    if (aText.isEmpty())
2127
0
        return;
2128
2129
0
    std::vector< OUString > aResultVec;
2130
0
    OUString aNew;
2131
0
    sal_Int32 nLongestPrefixLen = 0;
2132
0
    miAutoPosColumn = pColumnData->end();
2133
0
    mbPartialPrefix = false;
2134
0
    miAutoPosColumn = findTextAll(*pColumnData, miAutoPosColumn, aText, aResultVec, false, &nLongestPrefixLen);
2135
2136
0
    if (nLongestPrefixLen <= 0 || aResultVec.empty())
2137
0
        return;
2138
2139
0
    if (aResultVec.size() > 1)
2140
0
    {
2141
0
        mbPartialPrefix = true;
2142
0
        bUseTab = true; // Allow Ctrl (+ Shift + ) + TAB cycling.
2143
0
        miAutoPosColumn = pColumnData->end();
2144
2145
        // Display the rest of longest common prefix as suggestion.
2146
0
        aNew = aResultVec[0].copy(0, nLongestPrefixLen);
2147
0
    }
2148
0
    else
2149
0
    {
2150
0
        aNew = aResultVec[0];
2151
0
    }
2152
2153
    // Strings can contain line endings (e.g. due to dBase import),
2154
    // which would result in multiple paragraphs here, which is not desirable.
2155
    //! Then GetExactMatch doesn't work either
2156
0
    lcl_RemoveLineEnd( aNew );
2157
2158
    // Keep paragraph, just append the rest
2159
    //! Exact replacement in EnterHandler !!!
2160
    // One Space between paragraphs:
2161
0
    sal_Int32 nEdLen = mpEditEngine->GetTextLen() + nParCnt - 1;
2162
0
    OUString aIns = aNew.copy(nEdLen);
2163
2164
    // Selection must be "backwards", so the cursor stays behind the last
2165
    // typed character
2166
0
    ESelection aSelection( aSel.end.nPara, aSel.end.nIndex + aIns.getLength(),
2167
0
                           aSel.end.nPara, aSel.end.nIndex );
2168
2169
    // When editing in input line, apply to both edit views
2170
0
    if ( pTableView )
2171
0
    {
2172
0
        pTableView->InsertText( aIns );
2173
0
        pTableView->SetSelection( aSelection );
2174
0
    }
2175
0
    if ( pTopView )
2176
0
    {
2177
0
        pTopView->InsertText( aIns );
2178
0
        pTopView->SetSelection( aSelection );
2179
0
    }
2180
2181
0
    aAutoSearch = aText; // To keep searching - nAutoPos is set
2182
0
}
2183
2184
void ScInputHandler::NextAutoEntry( bool bBack )
2185
0
{
2186
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
2187
0
    if ( pActiveView && pColumnData )
2188
0
    {
2189
0
        if (!aAutoSearch.isEmpty())
2190
0
        {
2191
            // Is the selection still valid (could be changed via the mouse)?
2192
0
            ESelection aSel = pActiveView->GetSelection();
2193
0
            aSel.Adjust();
2194
0
            sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
2195
0
            if ( aSel.end.nPara+1 == nParCnt && aSel.start.nPara == aSel.end.nPara )
2196
0
            {
2197
0
                OUString aText = GetEditText(mpEditEngine.get());
2198
0
                sal_Int32 nSelLen = aSel.end.nIndex - aSel.start.nIndex;
2199
0
                sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.end.nPara );
2200
0
                if ( aSel.end.nIndex == nParLen && aText.getLength() == aAutoSearch.getLength() + nSelLen )
2201
0
                {
2202
0
                    OUString aNew;
2203
0
                    ScTypedCaseStrSet::const_iterator itNew =
2204
0
                        findText(*pColumnData, miAutoPosColumn, aAutoSearch, aNew, bBack);
2205
2206
0
                    if (itNew != pColumnData->end())
2207
0
                    {
2208
                        // match found!
2209
0
                        miAutoPosColumn = itNew;
2210
0
                        bInOwnChange = true;        // disable ModifyHdl (reset below)
2211
0
                        mbPartialPrefix = false;
2212
2213
0
                        lcl_RemoveLineEnd( aNew );
2214
0
                        OUString aIns = aNew.copy(aAutoSearch.getLength());
2215
2216
                        //  when editing in input line, apply to both edit views
2217
0
                        if ( pTableView )
2218
0
                        {
2219
0
                            pTableView->DeleteSelected();
2220
0
                            pTableView->InsertText( aIns );
2221
0
                            pTableView->SetSelection( ESelection(
2222
0
                                                        aSel.end.nPara, aSel.start.nIndex + aIns.getLength(),
2223
0
                                                        aSel.end.nPara, aSel.start.nIndex ) );
2224
0
                        }
2225
0
                        if ( pTopView )
2226
0
                        {
2227
0
                            pTopView->DeleteSelected();
2228
0
                            pTopView->InsertText( aIns );
2229
0
                            pTopView->SetSelection( ESelection(
2230
0
                                                        aSel.end.nPara, aSel.start.nIndex + aIns.getLength(),
2231
0
                                                        aSel.end.nPara, aSel.start.nIndex ) );
2232
0
                        }
2233
2234
0
                        bInOwnChange = false;
2235
0
                    }
2236
0
                }
2237
0
            }
2238
0
        }
2239
0
    }
2240
2241
    // For Tab, HideCursor was always called first
2242
0
    if (pActiveView)
2243
0
        pActiveView->ShowCursor();
2244
0
}
2245
2246
// Highlight parentheses
2247
void ScInputHandler::UpdateParenthesis()
2248
0
{
2249
    // Find parentheses
2250
    //TODO: Can we disable parentheses highlighting per parentheses?
2251
0
    bool bFound = false;
2252
0
    if ( bFormulaMode && eMode != SC_INPUT_TOP )
2253
0
    {
2254
0
        if ( pTableView && !pTableView->HasSelection() ) // Selection is always at the bottom
2255
0
        {
2256
0
            ESelection aSel = pTableView->GetSelection();
2257
0
            if (aSel.start.nIndex)
2258
0
            {
2259
                // Examine character left to the cursor
2260
0
                sal_Int32 nPos = aSel.start.nIndex - 1;
2261
0
                OUString aFormula = mpEditEngine->GetText(aSel.start.nPara);
2262
0
                sal_Unicode c = aFormula[nPos];
2263
0
                if ( c == '(' || c == ')' )
2264
0
                {
2265
                    // Note this matches only within one paragraph.
2266
0
                    sal_Int32 nOther = lcl_MatchParenthesis( aFormula, nPos );
2267
0
                    if ( nOther != -1 )
2268
0
                    {
2269
0
                        SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() );
2270
0
                        aSet.Put( SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT ) );
2271
2272
                        //! Distinguish if cell is already highlighted!!!!
2273
0
                        if (bParenthesisShown)
2274
0
                        {
2275
                            // Remove old highlighting
2276
0
                            sal_Int32 nCount = mpEditEngine->GetParagraphCount();
2277
0
                            for (sal_Int32 i=0; i<nCount; i++)
2278
0
                                mpEditEngine->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
2279
0
                        }
2280
2281
0
                        ESelection aSelThis(aSel.start.nPara, nPos, aSel.start.nPara, nPos + 1);
2282
0
                        mpEditEngine->QuickSetAttribs( aSet, aSelThis );
2283
0
                        ESelection aSelOther(aSel.start.nPara, nOther, aSel.start.nPara, nOther + 1);
2284
0
                        mpEditEngine->QuickSetAttribs( aSet, aSelOther );
2285
2286
                        // Dummy InsertText for Update and Paint (selection is empty)
2287
0
                        pTableView->InsertText( OUString() );
2288
2289
0
                        bFound = true;
2290
0
                    }
2291
0
                }
2292
0
            }
2293
2294
            //  mark parenthesis right of cursor if it will be overwritten (nAutoPar)
2295
            //  with different color (COL_LIGHTBLUE) ??
2296
0
        }
2297
0
    }
2298
2299
    // Remove old highlighting, if no new one is set
2300
0
    if ( bParenthesisShown && !bFound && pTableView )
2301
0
    {
2302
0
        sal_Int32 nCount = mpEditEngine->GetParagraphCount();
2303
0
        for (sal_Int32 i=0; i<nCount; i++)
2304
0
            pTableView->RemoveCharAttribs( i, EE_CHAR_WEIGHT );
2305
0
    }
2306
2307
0
    bParenthesisShown = bFound;
2308
0
}
2309
2310
void ScInputHandler::ViewShellGone(const ScTabViewShell* pViewSh) // Executed synchronously!
2311
0
{
2312
0
    if ( pViewSh == pActiveViewSh )
2313
0
    {
2314
0
        pLastState.reset();
2315
0
        pLastPattern = nullptr;
2316
0
    }
2317
2318
0
    ScModule* mod = ScModule::get();
2319
0
    if ( pViewSh == pRefViewSh )
2320
0
    {
2321
        //! The input from the EnterHandler does not arrive anymore
2322
        // We end the EditMode anyways
2323
0
        EnterHandler();
2324
0
        bFormulaMode = false;
2325
0
        pRefViewSh = nullptr;
2326
0
        SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
2327
0
        mod->SetRefInputHdl(nullptr);
2328
0
        if (pInputWin)
2329
0
            pInputWin->SetFormulaMode(false);
2330
0
        UpdateAutoCorrFlag();
2331
0
    }
2332
2333
0
    pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current()  );
2334
2335
0
    if ( pActiveViewSh && pActiveViewSh == pViewSh )
2336
0
    {
2337
0
        OSL_FAIL("pActiveViewSh is gone");
2338
0
        pActiveViewSh = nullptr;
2339
0
    }
2340
2341
0
    if (mod->GetInputOptions().GetTextWysiwyg())
2342
0
        UpdateRefDevice(); // Don't keep old document's printer as RefDevice
2343
0
}
2344
2345
void ScInputHandler::UpdateActiveView()
2346
0
{
2347
0
    ImplCreateEditEngine();
2348
2349
    // #i20588# Don't rely on focus to find the active edit view. Instead, the
2350
    // active pane at the start of editing is now stored (GetEditActivePart).
2351
    // GetActiveWin (the currently active pane) fails for ref input across the
2352
    // panes of a split view.
2353
2354
0
    vcl::Window* pShellWin = pActiveViewSh ?
2355
0
                pActiveViewSh->GetWindowByPos( pActiveViewSh->GetViewData().GetEditActivePart() ) :
2356
0
                nullptr;
2357
2358
0
    sal_uInt16 nCount = mpEditEngine->GetViewCount();
2359
0
    if (nCount > 0)
2360
0
    {
2361
0
        pTableView = mpEditEngine->GetView();
2362
0
        for (sal_uInt16 i=1; i<nCount; i++)
2363
0
        {
2364
0
            EditView* pThis = mpEditEngine->GetView(i);
2365
0
            vcl::Window* pWin = pThis->GetWindow();
2366
0
            if ( pWin==pShellWin )
2367
0
                pTableView = pThis;
2368
0
        }
2369
0
    }
2370
0
    else
2371
0
        pTableView = nullptr;
2372
2373
    // setup the pTableView editeng for tiled rendering to get cursor and selections
2374
0
    if (pTableView && pActiveViewSh)
2375
0
    {
2376
0
        if (comphelper::LibreOfficeKit::isActive())
2377
0
        {
2378
0
            pTableView->RegisterViewShell(pActiveViewSh);
2379
0
        }
2380
0
    }
2381
2382
0
    if (pInputWin && (eMode == SC_INPUT_TOP || eMode == SC_INPUT_TABLE))
2383
0
    {
2384
        // tdf#71409: Always create the edit engine instance for the input
2385
        // window, in order to properly manage accessibility events.
2386
0
        pTopView = pInputWin->GetEditView();
2387
0
        if (eMode != SC_INPUT_TOP)
2388
0
            pTopView = nullptr;
2389
0
    }
2390
0
    else
2391
0
        pTopView = nullptr;
2392
0
}
2393
2394
void ScInputHandler::SetInputWindow(  ScInputWindow* pNew )
2395
0
{
2396
0
    pInputWin = pNew;
2397
0
}
2398
2399
void ScInputHandler::StopInputWinEngine( bool bAll )
2400
0
{
2401
0
    if (pInputWin && !pInputWin->isDisposed())
2402
0
        pInputWin->StopEditEngine( bAll );
2403
2404
0
    pTopView = nullptr; // invalid now
2405
0
}
2406
2407
EditView* ScInputHandler::GetActiveView()
2408
0
{
2409
0
    UpdateActiveView();
2410
0
    return pTopView ? pTopView : pTableView;
2411
0
}
2412
2413
void ScInputHandler::ForgetLastPattern()
2414
0
{
2415
0
    pLastPattern = nullptr;
2416
0
    if ( !pLastState && pActiveViewSh )
2417
0
        pActiveViewSh->UpdateInputHandler( true ); // Get status again
2418
0
    else
2419
0
        NotifyChange( pLastState.get(), true );
2420
0
}
2421
2422
void ScInputHandler::UpdateAdjust( sal_Unicode cTyped )
2423
0
{
2424
0
    SvxAdjust eSvxAdjust;
2425
0
    switch (eAttrAdjust)
2426
0
    {
2427
0
        case SvxCellHorJustify::Standard:
2428
0
            {
2429
0
                bool bNumber = false;
2430
0
                if (cTyped)                                     // Restarted
2431
0
                    bNumber = (cTyped>='0' && cTyped<='9');     // Only ciphers are numbers
2432
0
                else if ( pActiveViewSh )
2433
0
                {
2434
0
                    ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
2435
0
                    bNumber = ( rDoc.GetCellType( aCursorPos ) == CELLTYPE_VALUE );
2436
0
                }
2437
0
                eSvxAdjust = bNumber ? SvxAdjust::Right : SvxAdjust::Left;
2438
0
            }
2439
0
            break;
2440
0
        case SvxCellHorJustify::Block:
2441
0
            eSvxAdjust = SvxAdjust::Block;
2442
0
            break;
2443
0
        case SvxCellHorJustify::Center:
2444
0
            eSvxAdjust = SvxAdjust::Center;
2445
0
            break;
2446
0
        case SvxCellHorJustify::Right:
2447
0
            eSvxAdjust = SvxAdjust::Right;
2448
0
            break;
2449
0
        default:    // SvxCellHorJustify::Left
2450
0
            eSvxAdjust = SvxAdjust::Left;
2451
0
            break;
2452
0
    }
2453
2454
0
    bool bAsianVertical = pLastPattern &&
2455
0
        pLastPattern->GetItem( ATTR_STACKED ).GetValue() &&
2456
0
        pLastPattern->GetItem( ATTR_VERTICAL_ASIAN ).GetValue();
2457
0
    if ( bAsianVertical )
2458
0
    {
2459
        // Always edit at top of cell -> LEFT when editing vertically
2460
0
        eSvxAdjust = SvxAdjust::Left;
2461
0
    }
2462
2463
0
    pEditDefaults->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) );
2464
0
    mpEditEngine->SetDefaults( *pEditDefaults );
2465
2466
0
    if ( pActiveViewSh )
2467
0
    {
2468
0
        pActiveViewSh->GetViewData().SetEditAdjust( eSvxAdjust );
2469
0
    }
2470
0
    mpEditEngine->SetVertical( bAsianVertical );
2471
0
}
2472
2473
void ScInputHandler::RemoveAdjust()
2474
0
{
2475
    // Delete hard alignment attributes
2476
0
    bool bUndo = mpEditEngine->IsUndoEnabled();
2477
0
    if ( bUndo )
2478
0
        mpEditEngine->EnableUndo( false );
2479
2480
    // Non-default paragraph attributes (e.g. from clipboard)
2481
    // must be turned into character attributes
2482
0
    mpEditEngine->RemoveParaAttribs();
2483
2484
0
    if ( bUndo )
2485
0
        mpEditEngine->EnableUndo( true );
2486
2487
0
}
2488
2489
void ScInputHandler::RemoveRangeFinder()
2490
0
{
2491
    // Delete pRangeFindList and colors
2492
0
    mpEditEngine->SetUpdateLayout(false);
2493
0
    sal_Int32 nCount = mpEditEngine->GetParagraphCount(); // Could just have been inserted
2494
0
    for (sal_Int32 i=0; i<nCount; i++)
2495
0
        mpEditEngine->RemoveCharAttribs( i, EE_CHAR_COLOR );
2496
0
    mpEditEngine->SetUpdateLayout(true);
2497
2498
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
2499
0
    pActiveView->ShowCursor( false );
2500
2501
0
    DeleteRangeFinder(); // Deletes the list and the labels on the table
2502
0
}
2503
2504
bool ScInputHandler::StartTable(sal_Unicode cTyped, bool bFromCommand, bool bInputActivated,
2505
        ScEditEngineDefaulter* pTopEngine, const ErrorHdl& errorHdl)
2506
0
{
2507
0
    bool bNewTable = false;
2508
2509
0
    if (bModified)
2510
0
        return false;
2511
2512
0
    if (pActiveViewSh)
2513
0
    {
2514
0
        ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument();
2515
2516
0
        if (!rDoc.ValidCol(aCursorPos.Col()))
2517
0
            return false;
2518
2519
0
        ImplCreateEditEngine();
2520
0
        UpdateActiveView();
2521
0
        SyncViews();
2522
2523
2524
0
        const ScMarkData& rMark = pActiveViewSh->GetViewData().GetMarkData();
2525
0
        ScEditableTester aTester;
2526
0
        if ( rMark.IsMarked() || rMark.IsMultiMarked() )
2527
0
            aTester.TestSelection( rDoc, rMark );
2528
0
        else
2529
0
            aTester.TestSelectedBlock(
2530
0
                rDoc, aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Col(), aCursorPos.Row(), rMark );
2531
2532
0
        bool bStartInputMode = !(pActiveViewSh->GetViewShell() && pActiveViewSh->GetViewShell()->IsLokReadOnlyView());
2533
2534
0
        if (!aTester.IsEditable())
2535
0
        {
2536
0
            bProtected = true;
2537
            // We allow read-only input mode activation regardless
2538
            // whether it's part of an array or not or whether explicit cell
2539
            // activation is requested (double-click or F2) or a click in input
2540
            // line.
2541
0
            bool bShowError = (!bInputActivated || !aTester.GetMessageId() || aTester.GetMessageId() != STR_PROTECTIONERR) &&
2542
0
                !pActiveViewSh->GetViewData().GetDocShell()->IsReadOnly();
2543
0
            if (bShowError)
2544
0
            {
2545
0
                eMode = SC_INPUT_NONE;
2546
0
                StopInputWinEngine( true );
2547
0
                UpdateFormulaMode();
2548
0
                if ( pActiveViewSh && ( !bFromCommand || !bCommandErrorShown ) )
2549
0
                {
2550
                    //  Prevent repeated error messages for the same cell from command events
2551
                    //  (for keyboard events, multiple messages are wanted).
2552
                    //  Set the flag before showing the error message because the command handler
2553
                    //  for the next IME command may be called when showing the dialog.
2554
0
                    if ( bFromCommand )
2555
0
                        bCommandErrorShown = true;
2556
2557
0
                    errorHdl(pActiveViewSh, aTester.GetMessageId());
2558
0
                }
2559
0
                bStartInputMode = false;
2560
0
            }
2561
0
        }
2562
2563
0
        if (bStartInputMode)
2564
0
        {
2565
            // UpdateMode is enabled again in ScViewData::SetEditEngine (and not needed otherwise)
2566
0
            mpEditEngine->SetUpdateLayout( false );
2567
2568
            // Take over attributes in EditEngine
2569
0
            const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(),
2570
0
                                                              aCursorPos.Row(),
2571
0
                                                              aCursorPos.Tab() );
2572
0
            if (!ScPatternAttr::areSame(pPattern, pLastPattern))
2573
0
            {
2574
                // Percent format?
2575
0
                const SfxItemSet& rAttrSet = pPattern->GetItemSet();
2576
2577
0
                if ( const SfxUInt32Item* pItem = rAttrSet.GetItemIfSet( ATTR_VALUE_FORMAT ) )
2578
0
                {
2579
0
                    sal_uInt32 nFormat = pItem->GetValue();
2580
0
                    if (SvNumFormatType::PERCENT == rDoc.GetFormatTable()->GetType( nFormat ))
2581
0
                        nCellPercentFormatDecSep = rDoc.GetFormatTable()->GetFormatDecimalSep( nFormat).toChar();
2582
0
                    else
2583
0
                        nCellPercentFormatDecSep = 0;
2584
0
                }
2585
0
                else
2586
0
                    nCellPercentFormatDecSep = 0; // Default: no percent
2587
2588
                // Validity specified?
2589
0
                if ( const SfxUInt32Item* pItem = rAttrSet.GetItemIfSet( ATTR_VALIDDATA ) )
2590
0
                    nValidation = pItem->GetValue();
2591
0
                else
2592
0
                    nValidation = 0;
2593
2594
                //  EditEngine Defaults
2595
                //  In no case SetParaAttribs, because the EditEngine might already
2596
                //  be filled (for Edit cells).
2597
                //  SetParaAttribs would change the content.
2598
2599
                //! The SetDefaults is now (since MUST/src602
2600
                //! EditEngine changes) implemented as a SetParaAttribs.
2601
                //! Any problems?
2602
2603
0
                pPattern->FillEditItemSet( pEditDefaults.get() );
2604
0
                mpEditEngine->SetDefaults( *pEditDefaults );
2605
0
                pLastPattern = pPattern;
2606
0
                bLastIsSymbol = pPattern->IsSymbolFont();
2607
2608
                //  Background color must be known for automatic font color.
2609
                //  For transparent cell background, the document background color must be used.
2610
2611
0
                Color aBackCol = pPattern->GetItem( ATTR_BACKGROUND ).GetColor();
2612
0
                ScModule* pScMod = ScModule::get();
2613
0
                if ( aBackCol.IsTransparent() ||
2614
0
                        Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
2615
0
                    aBackCol = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
2616
0
                mpEditEngine->SetBackgroundColor( aBackCol );
2617
2618
                // Adjustment
2619
0
                eAttrAdjust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
2620
0
                if ( eAttrAdjust == SvxCellHorJustify::Repeat &&
2621
0
                     pPattern->GetItem(ATTR_LINEBREAK).GetValue() )
2622
0
                {
2623
                    // #i31843# "repeat" with "line breaks" is treated as default alignment
2624
0
                    eAttrAdjust = SvxCellHorJustify::Standard;
2625
0
                }
2626
0
            }
2627
2628
0
            if (pTopEngine)
2629
0
            {
2630
                // Necessary to sync SvxAutoCorrect behavior. This has to be
2631
                // done before InitRangeFinder() below.
2632
0
                MergeLanguageAttributes( *pTopEngine);
2633
0
            }
2634
2635
            //  UpdateSpellSettings enables online spelling if needed
2636
            //  -> also call if attributes are unchanged
2637
0
            UpdateSpellSettings( true ); // uses pLastPattern
2638
2639
            // Fill EditEngine
2640
0
            OUString aStr;
2641
0
            if (bTextValid)
2642
0
            {
2643
0
                mpEditEngine->SetTextCurrentDefaults(aCurrentText);
2644
0
                aStr = aCurrentText;
2645
0
                bTextValid = false;
2646
0
                aCurrentText.clear();
2647
0
            }
2648
0
            else
2649
0
                aStr = GetEditText(mpEditEngine.get());
2650
2651
            // cTyped!=0 is overtyping, not editing.
2652
0
            mbEditingExistingContent = !cTyped && !aStr.isEmpty();
2653
2654
0
            if (aStr.startsWith("{=") && aStr.endsWith("}") )  // Matrix formula?
2655
0
            {
2656
0
                monPrevBlockMode = ScEnterMode::MATRIX;
2657
0
                aStr = aStr.copy(1, aStr.getLength() -2);
2658
0
                mpEditEngine->SetTextCurrentDefaults(aStr);
2659
0
                if ( pInputWin )
2660
0
                    pInputWin->SetTextString(aStr, true);
2661
0
            }
2662
0
            else
2663
0
                monPrevBlockMode = ScEnterMode::NORMAL;
2664
2665
0
            UpdateAdjust( cTyped );
2666
2667
0
            if (ScModule::get()->GetAppOptions().GetAutoComplete())
2668
0
                GetColData();
2669
2670
0
            if (!cTyped && !bCreatingFuncView && StartsLikeFormula(aStr))
2671
0
                InitRangeFinder(aStr); // Formula is being edited -> RangeFinder
2672
2673
0
            bNewTable = true; // -> PostEditView Call
2674
0
        }
2675
0
    }
2676
2677
0
    if (!bProtected && pInputWin)
2678
0
        pInputWin->SetOkCancelMode();
2679
2680
0
    return bNewTable;
2681
0
}
2682
2683
void ScInputHandler::MergeLanguageAttributes( ScEditEngineDefaulter& rDestEngine ) const
2684
0
{
2685
0
    const SfxItemSet& rSrcSet = mpEditEngine->GetDefaults();
2686
0
    rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE ));
2687
0
    rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CJK ));
2688
0
    rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CTL ));
2689
0
}
2690
2691
static void lcl_SetTopSelection( EditView* pEditView, ESelection& rSel )
2692
0
{
2693
0
    OSL_ENSURE( rSel.start.nPara==0 && rSel.end.nPara==0, "SetTopSelection: Para != 0" );
2694
2695
0
    EditEngine& rEngine = pEditView->getEditEngine();
2696
0
    sal_Int32 nCount = rEngine.GetParagraphCount();
2697
0
    if (nCount > 1)
2698
0
    {
2699
0
        sal_Int32 nParLen = rEngine.GetTextLen(rSel.start.nPara);
2700
0
        while (rSel.start.nIndex > nParLen && rSel.start.nPara + 1 < nCount)
2701
0
        {
2702
0
            rSel.start.nIndex -= nParLen + 1; // Including space from line break
2703
0
            nParLen = rEngine.GetTextLen(++rSel.start.nPara);
2704
0
        }
2705
2706
0
        nParLen = rEngine.GetTextLen(rSel.end.nPara);
2707
0
        while (rSel.end.nIndex > nParLen && rSel.end.nPara + 1 < nCount)
2708
0
        {
2709
0
            rSel.end.nIndex -= nParLen + 1; // Including space from line break
2710
0
            nParLen = rEngine.GetTextLen(++rSel.end.nPara);
2711
0
        }
2712
0
    }
2713
2714
0
    ESelection aSel = pEditView->GetSelection();
2715
2716
0
    if (rSel != aSel)
2717
0
        pEditView->SetSelection( rSel );
2718
0
}
2719
2720
void ScInputHandler::SyncViews( const EditView* pSourceView )
2721
0
{
2722
0
    if (pSourceView)
2723
0
    {
2724
0
        bool bSelectionForTopView = false;
2725
0
        if (pTopView && pTopView != pSourceView)
2726
0
            bSelectionForTopView = true;
2727
0
        bool bSelectionForTableView = false;
2728
0
        if (pTableView && pTableView != pSourceView)
2729
0
            bSelectionForTableView = true;
2730
0
        if (bSelectionForTopView || bSelectionForTableView)
2731
0
        {
2732
0
            ESelection aSel(pSourceView->GetSelection());
2733
0
            if (bSelectionForTopView)
2734
0
                pTopView->SetSelection(aSel);
2735
0
            if (bSelectionForTableView)
2736
0
                lcl_SetTopSelection(pTableView, aSel);
2737
0
        }
2738
0
    }
2739
    // Only sync selection from topView if we are actually editing there
2740
0
    else if (pTopView && pTableView)
2741
0
    {
2742
0
        ESelection aSel(pTopView->GetSelection());
2743
0
        lcl_SetTopSelection( pTableView, aSel );
2744
0
    }
2745
0
}
2746
2747
IMPL_LINK_NOARG(ScInputHandler, ModifyHdl, LinkParamNone*, void)
2748
0
{
2749
0
    if ( !bInOwnChange && ( eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE ) &&
2750
0
         mpEditEngine && mpEditEngine->IsUpdateLayout() && pInputWin )
2751
0
    {
2752
        // Update input line from ModifyHdl for changes that are not
2753
        // wrapped by DataChanging/DataChanged calls (like Drag&Drop)
2754
0
        OUString aText(ScEditUtil::GetMultilineString(*mpEditEngine));
2755
0
        lcl_RemoveTabs(aText);
2756
0
        pInputWin->SetTextString(aText, true);
2757
0
    }
2758
0
}
2759
2760
//static
2761
void ScInputHandler::ErrorMessage(ScTabViewShell* pActiveViewShell, TranslateId messageId)
2762
0
{
2763
0
    pActiveViewShell->GetActiveWin()->GrabFocus();
2764
0
    pActiveViewShell->ErrorMessage(messageId);
2765
0
}
2766
2767
/**
2768
 * @return true means new view created
2769
 */
2770
bool ScInputHandler::DataChanging( sal_Unicode cTyped, bool bFromCommand )
2771
0
{
2772
0
    if (pActiveViewSh)
2773
0
        pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE );
2774
0
    bInOwnChange = true; // disable ModifyHdl (reset in DataChanged)
2775
2776
0
    if ( eMode == SC_INPUT_NONE )
2777
0
        return StartTable(cTyped, bFromCommand, false, nullptr, ScInputHandler::ErrorMessage);
2778
0
    else
2779
0
        return false;
2780
0
}
2781
2782
void ScInputHandler::DataChanged( bool bFromTopNotify, bool bSetModified )
2783
0
{
2784
0
    ImplCreateEditEngine();
2785
2786
0
    if (eMode==SC_INPUT_NONE)
2787
0
        eMode = SC_INPUT_TYPE;
2788
2789
0
    if ( eMode == SC_INPUT_TOP && pTopView && !bFromTopNotify )
2790
0
    {
2791
        //  table EditEngine is formatted below, input line needs formatting after paste
2792
        //  #i20282# not when called from the input line's modify handler
2793
0
        pTopView->getEditEngine().QuickFormatDoc( true );
2794
2795
        //  #i23720# QuickFormatDoc hides the cursor, but can't show it again because it
2796
        //  can't safely access the EditEngine's current view, so the cursor has to be
2797
        //  shown again here.
2798
0
        pTopView->ShowCursor();
2799
0
    }
2800
2801
0
    if (bSetModified)
2802
0
        bModified = true;
2803
0
    bSelIsRef = false;
2804
2805
0
    if ( pRangeFindList && !bInRangeUpdate )
2806
0
        RemoveRangeFinder(); // Delete attributes and labels
2807
2808
0
    UpdateParenthesis(); // Highlight parentheses anew
2809
2810
0
    const bool bUpdateKit = comphelper::LibreOfficeKit::isActive() && pActiveViewSh && pInputWin;
2811
2812
0
    if (eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE)
2813
0
    {
2814
0
        OUString aText;
2815
0
        if (pInputWin)
2816
0
            aText = ScEditUtil::GetMultilineString(*mpEditEngine);
2817
0
        else
2818
0
            aText = GetEditText(mpEditEngine.get());
2819
0
        lcl_RemoveTabs(aText);
2820
2821
0
        if (pInputWin)
2822
0
        {
2823
            // If we will end up updating LoKit at the end, we can skip it here
2824
0
            pInputWin->SetTextString(aText, !bUpdateKit);
2825
0
        }
2826
0
    }
2827
2828
    // If the cursor is before the end of a paragraph, parts are being pushed to
2829
    // the right (independently from the eMode) -> Adapt View!
2830
    // If the cursor is at the end, the StatusHandler of the ViewData is sufficient.
2831
    //
2832
    // First make sure the status handler is called now if the cursor
2833
    // is outside the visible area
2834
0
    mpEditEngine->QuickFormatDoc();
2835
2836
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
2837
0
    ESelection aSel;
2838
0
    if (pActiveView && pActiveViewSh)
2839
0
    {
2840
0
        ScViewData& rViewData = pActiveViewSh->GetViewData();
2841
2842
0
        bool bNeedGrow = ( rViewData.GetEditAdjust() != SvxAdjust::Left ); // Always right-aligned
2843
0
        if (!bNeedGrow)
2844
0
        {
2845
            // Cursor before the end?
2846
0
            aSel = pActiveView->GetSelection();
2847
0
            aSel.Adjust();
2848
0
            bNeedGrow = (aSel.end.nIndex != mpEditEngine->GetTextLen(aSel.end.nPara));
2849
0
        }
2850
0
        if (!bNeedGrow)
2851
0
        {
2852
0
            bNeedGrow = rViewData.GetDocument().IsLayoutRTL( rViewData.CurrentTabForData() );
2853
0
        }
2854
0
        if (bNeedGrow)
2855
0
        {
2856
            // Adjust inplace view
2857
0
            rViewData.EditGrowY();
2858
0
            rViewData.EditGrowX();
2859
0
        }
2860
0
    }
2861
2862
0
    if (bUpdateKit)
2863
0
    {
2864
0
        UpdateActiveView();
2865
2866
0
        if (pActiveView)
2867
0
        {
2868
0
            pActiveView->ShowCursor(); // Send show cursor command after registering the view.
2869
0
            aSel = pActiveView->GetSelection();
2870
0
        }
2871
2872
0
        OUString aText = ScEditUtil::GetMultilineString(*mpEditEngine);
2873
0
        pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aText.toUtf8());
2874
0
        pActiveViewSh->LOKSendFormulabarUpdate(pActiveView,
2875
0
                                               aText,
2876
0
                                               aSel);
2877
0
    }
2878
2879
0
    UpdateFormulaMode();
2880
0
    bTextValid = false; // Changes only in the EditEngine
2881
0
    bInOwnChange = false;
2882
0
}
2883
2884
bool ScInputHandler::StartsLikeFormula( std::u16string_view rStr ) const
2885
0
{
2886
    // For new input '+' and '-' may start the dreaded "lazy data typist"
2887
    // formula input, editing existing formula content can only start with '='.
2888
0
    return !rStr.empty() && (rStr[0] == '=' || (!mbEditingExistingContent && (rStr[0] == '+' || rStr[0] == '-')));
2889
0
}
2890
2891
void ScInputHandler::UpdateFormulaMode()
2892
0
{
2893
0
    SfxApplication* pSfxApp = SfxGetpApp();
2894
2895
0
    if (!pActiveViewSh)
2896
0
        return;
2897
2898
0
    bool bIsFormula = !bProtected;
2899
0
    if (bIsFormula)
2900
0
    {
2901
0
        const OUString aText = mpEditEngine->GetText(0);
2902
0
        bIsFormula = StartsLikeFormula(aText);
2903
0
    }
2904
2905
0
    if ( bIsFormula )
2906
0
    {
2907
0
        if (!bFormulaMode)
2908
0
        {
2909
0
            pActiveViewSh->GetViewData().SetEditHighlight(true);
2910
0
            bFormulaMode = true;
2911
0
            pRefViewSh = pActiveViewSh;
2912
0
            pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
2913
0
            ScModule* pMod = ScModule::get();
2914
0
            pMod->SetRefInputHdl(this);
2915
0
            if (pInputWin)
2916
0
                pInputWin->SetFormulaMode(true);
2917
2918
            // in LOK, we always need to perform the GetFormulaData() call so
2919
            // that the formula insertion works
2920
0
            if (comphelper::LibreOfficeKit::isActive() || pMod->GetAppOptions().GetAutoComplete())
2921
0
                GetFormulaData();
2922
2923
0
            UpdateParenthesis();
2924
0
            UpdateAutoCorrFlag();
2925
0
        }
2926
0
    }
2927
0
    else // Deactivate
2928
0
    {
2929
0
        if (bFormulaMode)
2930
0
        {
2931
0
            pActiveViewSh->GetViewData().SetEditHighlight(false);
2932
0
            ShowRefFrame();
2933
0
            bFormulaMode = false;
2934
0
            pRefViewSh = nullptr;
2935
0
            pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
2936
0
            ScModule::get()->SetRefInputHdl(nullptr);
2937
0
            if (pInputWin)
2938
0
                pInputWin->SetFormulaMode(false);
2939
0
            UpdateAutoCorrFlag();
2940
0
        }
2941
0
    }
2942
0
}
2943
2944
void ScInputHandler::ShowRefFrame()
2945
0
{
2946
    // Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat
2947
    // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh.
2948
    // A local variable is used instead.
2949
0
    ScTabViewShell* pVisibleSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current()  );
2950
0
    if ( !(pRefViewSh && pRefViewSh != pVisibleSh) )
2951
0
        return;
2952
2953
0
    bool bFound = false;
2954
0
    SfxViewFrame& rRefFrame = pRefViewSh->GetViewFrame();
2955
0
    SfxViewFrame* pOneFrame = SfxViewFrame::GetFirst();
2956
0
    while ( pOneFrame && !bFound )
2957
0
    {
2958
0
        if ( pOneFrame == &rRefFrame )
2959
0
            bFound = true;
2960
0
        pOneFrame = SfxViewFrame::GetNext( *pOneFrame );
2961
0
    }
2962
2963
0
    if (bFound)
2964
0
    {
2965
        // We count on Activate working synchronously here
2966
        // (pActiveViewSh is set while doing so)
2967
0
        pRefViewSh->SetActive(); // Appear and SetViewFrame
2968
2969
        //  pLastState is set correctly in the NotifyChange from the Activate
2970
0
    }
2971
0
    else
2972
0
    {
2973
0
        OSL_FAIL("ViewFrame for reference input is not here anymore");
2974
0
    }
2975
0
}
2976
2977
void ScInputHandler::RemoveSelection()
2978
0
{
2979
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
2980
0
    if (!pActiveView)
2981
0
        return;
2982
2983
0
    ESelection aSel = pActiveView->GetSelection();
2984
0
    aSel.CollapseToEnd();
2985
0
    if (pTableView)
2986
0
        pTableView->SetSelection( aSel );
2987
0
    if (pTopView)
2988
0
        pTopView->SetSelection( aSel );
2989
0
}
2990
2991
void ScInputHandler::InvalidateAttribs()
2992
0
{
2993
0
    SfxViewFrame* pViewFrm = SfxViewFrame::Current();
2994
0
    if (!pViewFrm)
2995
0
        return;
2996
2997
0
    SfxBindings& rBindings = pViewFrm->GetBindings();
2998
2999
0
    rBindings.Invalidate( SID_ATTR_CHAR_FONT );
3000
0
    rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
3001
0
    rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
3002
3003
0
    rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
3004
0
    rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
3005
0
    rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
3006
0
    rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
3007
0
    rBindings.Invalidate( SID_ULINE_VAL_NONE );
3008
0
    rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
3009
0
    rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
3010
0
    rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
3011
3012
0
    rBindings.Invalidate( SID_HYPERLINK_GETLINK );
3013
3014
0
    rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
3015
0
    rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
3016
0
    rBindings.Invalidate( SID_SET_SUB_SCRIPT );
3017
0
    rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
3018
0
    rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
3019
3020
0
    rBindings.Invalidate( SID_SAVEDOC );
3021
0
    rBindings.Invalidate( SID_DOC_MODIFIED );
3022
0
}
3023
3024
// --------------- public methods --------------------------------------------
3025
3026
void ScInputHandler::SetMode(ScInputMode eNewMode, const OUString* pInitText, ScEditEngineDefaulter* pTopEngine,
3027
                             const ErrorHdl& errorHdl)
3028
0
{
3029
0
    if ( eMode == eNewMode )
3030
0
        return;
3031
3032
0
    if (!pActiveViewSh)
3033
0
        return;
3034
3035
0
    ImplCreateEditEngine();
3036
3037
0
    if (bProtected)
3038
0
    {
3039
0
        eMode = SC_INPUT_NONE;
3040
0
        StopInputWinEngine( true );
3041
0
        pActiveViewSh->GetActiveWin()->GrabFocus();
3042
0
        return;
3043
0
    }
3044
3045
0
    if (eNewMode != SC_INPUT_NONE)
3046
        // Disable paste mode when edit mode starts.
3047
0
        pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE );
3048
3049
0
    bInOwnChange = true; // disable ModifyHdl (reset below)
3050
3051
0
    ScInputMode eOldMode = eMode;
3052
0
    eMode = eNewMode;
3053
0
    if (eOldMode == SC_INPUT_TOP && eNewMode != eOldMode)
3054
0
        StopInputWinEngine( false );
3055
3056
0
    if (eMode==SC_INPUT_TOP || eMode==SC_INPUT_TABLE)
3057
0
    {
3058
0
        if (eOldMode == SC_INPUT_NONE) // not if switching between modes
3059
0
        {
3060
0
            if (StartTable(0, false, eMode == SC_INPUT_TABLE, pTopEngine, errorHdl))
3061
0
            {
3062
0
                pActiveViewSh->GetViewData().GetDocShell()->PostEditView(*mpEditEngine, aCursorPos);
3063
0
            }
3064
0
        }
3065
3066
0
        if (pInitText)
3067
0
        {
3068
0
            mpEditEngine->SetTextCurrentDefaults(*pInitText);
3069
0
            bModified = true;
3070
0
        }
3071
3072
0
        sal_Int32 nPara = mpEditEngine->GetParagraphCount()-1;
3073
0
        sal_Int32 nLen = mpEditEngine->GetText(nPara).getLength();
3074
0
        sal_uInt16 nCount = mpEditEngine->GetViewCount();
3075
3076
0
        for (sal_uInt16 i=0; i<nCount; i++)
3077
0
        {
3078
0
            if ( eMode == SC_INPUT_TABLE && eOldMode == SC_INPUT_TOP )
3079
0
            {
3080
                // Keep Selection
3081
0
            }
3082
0
            else
3083
0
            {
3084
0
                mpEditEngine->GetView(i)->SetSelection(ESelection(nPara, nLen));
3085
0
            }
3086
0
            mpEditEngine->GetView(i)->ShowCursor(false);
3087
0
        }
3088
0
    }
3089
3090
0
    UpdateActiveView();
3091
0
    if (eMode==SC_INPUT_TABLE || eMode==SC_INPUT_TYPE)
3092
0
    {
3093
0
        if (pTableView)
3094
0
            pTableView->SetEditEngineUpdateLayout(true);
3095
0
        pActiveViewSh->GetViewData().SetEditHighlight(true);
3096
0
    }
3097
0
    else
3098
0
    {
3099
0
        if (pTopView)
3100
0
            pTopView->SetEditEngineUpdateLayout(true);
3101
0
    }
3102
3103
0
    if (eNewMode != eOldMode)
3104
0
        UpdateFormulaMode();
3105
3106
0
    bInOwnChange = false;
3107
0
}
3108
3109
void ScInputHandler::StartOrToggleEditMode()
3110
0
{
3111
    // Switch to SC_INPUT_TABLE mode unless we’re already in that mode
3112
    // in which case we’ll switch to SC_INPUT_TYPE. This has the
3113
    // effect of toggling between those two modes if this is called
3114
    // multiple times.
3115
0
    ScInputMode eNewMode = eMode == SC_INPUT_TABLE
3116
0
        ? SC_INPUT_TYPE
3117
0
        : SC_INPUT_TABLE;
3118
0
    SetMode(eNewMode);
3119
0
}
3120
3121
/**
3122
 * @return true if rString only contains digits (no autocorrect then)
3123
 */
3124
static bool lcl_IsNumber(std::u16string_view aString)
3125
0
{
3126
0
    size_t nLen = aString.size();
3127
0
    for (size_t i=0; i<nLen; i++)
3128
0
    {
3129
0
        sal_Unicode c = aString[i];
3130
0
        if ( c < '0' || c > '9' )
3131
0
            return false;
3132
0
    }
3133
0
    return true;
3134
0
}
3135
3136
static void lcl_SelectionToEnd( EditView* pView )
3137
0
{
3138
0
    if ( pView )
3139
0
        pView->SetSelection(ESelection::AtEnd());
3140
0
}
3141
3142
void ScInputHandler::EnterHandler( ScEnterMode nBlockMode, bool bBeforeSavingInLOK )
3143
0
{
3144
0
    if (!mbDocumentDisposing && comphelper::LibreOfficeKit::isActive()
3145
0
        && pActiveViewSh != SfxViewShell::Current())
3146
0
        return;
3147
3148
0
    if (!pActiveViewSh)
3149
0
        return;
3150
3151
    // Macro calls for validity can cause a lot of problems, so inhibit
3152
    // nested calls of EnterHandler().
3153
0
    if (bInEnterHandler) return;
3154
0
    bInEnterHandler = true;
3155
0
    bInOwnChange = true; // disable ModifyHdl (reset below)
3156
0
    mbPartialPrefix = false;
3157
    // tdf#104888: Normal mode => Matrix mode should be treated as a modification of the formula
3158
0
    if( !bModified && monPrevBlockMode && monPrevBlockMode.value() == ScEnterMode::NORMAL && nBlockMode == ScEnterMode::MATRIX )
3159
0
        bModified = true;
3160
0
    monPrevBlockMode.reset();
3161
3162
0
    ImplCreateEditEngine();
3163
3164
0
    OUString aString = GetEditText(mpEditEngine.get());
3165
0
    OUString aPreAutoCorrectString(aString);
3166
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
3167
0
    if (bModified && pActiveView && !aString.isEmpty() && !lcl_IsNumber(aString))
3168
0
    {
3169
0
        if (pColumnData && miAutoPosColumn != pColumnData->end())
3170
0
        {
3171
            // #i47125# If AutoInput appended something, do the final AutoCorrect
3172
            // with the cursor at the end of the input.
3173
0
            lcl_SelectionToEnd(pTopView);
3174
0
            lcl_SelectionToEnd(pTableView);
3175
0
        }
3176
3177
0
        vcl::Window* pFrameWin = pActiveViewSh->GetFrameWin();
3178
3179
0
        if (pTopView)
3180
0
            pTopView->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views
3181
0
        if (pTableView)
3182
0
            pTableView->CompleteAutoCorrect(pFrameWin);
3183
0
        aString = GetEditText(mpEditEngine.get());
3184
0
    }
3185
0
    lcl_RemoveTabs(aString);
3186
0
    lcl_RemoveTabs(aPreAutoCorrectString);
3187
3188
    // Test if valid (always with simple string)
3189
0
    if (bModified && nValidation)
3190
0
    {
3191
0
        ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
3192
0
        const ScValidationData* pData = rDoc.GetValidationEntry( nValidation );
3193
0
        if (pData)
3194
0
        {
3195
            // #i67990# don't use pLastPattern in EnterHandler
3196
0
            const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
3197
3198
0
            bool bOk;
3199
3200
0
            if (pData->GetDataMode() == SC_VALID_CUSTOM)
3201
0
            {
3202
0
                bOk = pData->IsDataValidCustom( aString, *pPattern, aCursorPos,  ScValidationData::CustomValidationPrivateAccess() );
3203
0
            }
3204
0
            else
3205
0
            {
3206
0
                bOk = pData->IsDataValid( aString, *pPattern, aCursorPos );
3207
0
            }
3208
3209
0
            if (!bOk)
3210
0
            {
3211
0
                pActiveViewSh->StopMarking();   // (the InfoBox consumes the MouseButtonUp)
3212
3213
                // tdf#125917 Release the grab that a current mouse-down event being handled
3214
                // by ScTabView has put on the mouse via its SelectionEngine.
3215
                // Otherwise the warning box cannot interact with the mouse
3216
0
                if (ScTabView* pView = pActiveViewSh->GetViewData().GetView())
3217
0
                {
3218
0
                    if (ScViewSelectionEngine* pSelEngine = pView->GetSelEngine())
3219
0
                        pSelEngine->ReleaseMouse();
3220
0
                }
3221
3222
0
                if (bBeforeSavingInLOK)
3223
0
                {
3224
                    // Invalid entry but not applied to the document model.
3225
                    // Exit to complete the "save", leaving the edit view as it is
3226
                    // for the user to continue after save.
3227
0
                    bInOwnChange = false;
3228
0
                    bInEnterHandler = false;
3229
0
                    return;
3230
0
                }
3231
3232
0
                pData->DoError(
3233
0
                    pActiveViewSh->GetFrameWeld(), aString, aCursorPos,
3234
0
                    [this, nBlockMode, aString, aPreAutoCorrectString](bool bForget)
3235
0
                    { EnterHandler2(nBlockMode, bForget, aString, aPreAutoCorrectString); });
3236
0
                return;
3237
0
            }
3238
0
        }
3239
0
    }
3240
0
    EnterHandler2(nBlockMode, false, aString, aPreAutoCorrectString);
3241
0
}
3242
3243
void ScInputHandler::EnterHandler2(ScEnterMode nBlockMode, bool bForget, OUString aString,
3244
                                   const OUString& aPreAutoCorrectString)
3245
0
{
3246
0
    std::unique_ptr<EditTextObject> pObject;
3247
0
    std::unique_ptr<ScPatternAttr> pCellAttrs;
3248
0
    bool bMatrix = (nBlockMode == ScEnterMode::MATRIX);
3249
0
    SfxApplication* pSfxApp = SfxGetpApp();
3250
3251
    // Check for input into DataPilot table
3252
0
    if ( bModified && !bForget )
3253
0
    {
3254
0
        ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
3255
0
        ScDPObject* pDPObj = rDoc.GetDPAtCursor( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
3256
0
        if ( pDPObj )
3257
0
        {
3258
            // Any input within the DataPilot table is either a valid renaming
3259
            // or an invalid action - normal cell input is always aborted
3260
0
            pActiveViewSh->DataPilotInput( aCursorPos, aString );
3261
0
            bForget = true;
3262
0
        }
3263
0
    }
3264
3265
0
    std::vector<editeng::MisspellRanges> aMisspellRanges;
3266
    // UpdateLayout must be true during CompleteOnlineSpelling
3267
0
    const bool bUpdateLayout = mpEditEngine->SetUpdateLayout( true );
3268
0
    mpEditEngine->CompleteOnlineSpelling();
3269
0
    bool bSpellErrors = !bFormulaMode && mpEditEngine->HasOnlineSpellErrors();
3270
0
    if ( bSpellErrors )
3271
0
    {
3272
        //  #i3820# If the spell checker flags numerical input as error,
3273
        //  it still has to be treated as number, not EditEngine object.
3274
0
        ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
3275
        // #i67990# don't use pLastPattern in EnterHandler
3276
0
        const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() );
3277
0
        if (pPattern)
3278
0
        {
3279
0
            SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
3280
            // without conditional format, as in ScColumn::SetString
3281
0
            sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
3282
0
            double nVal;
3283
0
            if ( pFormatter->IsNumberFormat( aString, nFormat, nVal ) )
3284
0
            {
3285
0
                bSpellErrors = false;       // ignore the spelling errors
3286
0
            }
3287
0
        }
3288
0
    }
3289
3290
    //  After RemoveAdjust, the EditView must not be repainted (has wrong font size etc).
3291
    //  SetUpdateLayout must come after CompleteOnlineSpelling.
3292
    //  The view is hidden in any case below (Broadcast).
3293
0
    mpEditEngine->SetUpdateLayout( false );
3294
3295
0
    if ( bModified && !bForget ) // What is being entered (text/object)?
3296
0
    {
3297
0
        sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
3298
0
        if ( nParCnt == 0 )
3299
0
            nParCnt = 1;
3300
3301
0
        bool bUniformAttribs = true;
3302
0
        SfxItemSet aPara1Attribs = mpEditEngine->GetAttribs(0, 0, mpEditEngine->GetTextLen(0));
3303
0
        for (sal_Int32 nPara = 1; nPara < nParCnt; ++nPara)
3304
0
        {
3305
0
            SfxItemSet aPara2Attribs = mpEditEngine->GetAttribs(nPara, 0, mpEditEngine->GetTextLen(nPara));
3306
0
            if (!(aPara1Attribs == aPara2Attribs))
3307
0
            {
3308
                // Paragraph format different from that of the 1st paragraph.
3309
0
                bUniformAttribs = false;
3310
0
                break;
3311
0
            }
3312
0
        }
3313
3314
0
        SfxItemSet aOldAttribs = mpEditEngine->GetAttribs(ESelection::All());
3315
0
        const SfxPoolItem* pItem = nullptr;
3316
3317
        // Find common (cell) attributes before RemoveAdjust
3318
0
        if ( bUniformAttribs )
3319
0
        {
3320
0
            std::optional<SfxItemSet> pCommonAttrs;
3321
0
            for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END; nId++)
3322
0
            {
3323
0
                SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem );
3324
0
                if ( eState == SfxItemState::SET &&
3325
0
                        nId != EE_CHAR_ESCAPEMENT && nId != EE_CHAR_PAIRKERNING &&
3326
0
                        nId != EE_CHAR_KERNING && nId != EE_CHAR_XMLATTRIBS &&
3327
0
                            *pItem != pEditDefaults->Get(nId) )
3328
0
                {
3329
0
                    if ( !pCommonAttrs )
3330
0
                        pCommonAttrs.emplace( mpEditEngine->GetEmptyItemSet() );
3331
0
                    pCommonAttrs->Put( *pItem );
3332
0
                }
3333
0
            }
3334
3335
0
            if ( pCommonAttrs )
3336
0
            {
3337
0
                ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument();
3338
0
                pCellAttrs = std::make_unique<ScPatternAttr>(rDoc.getCellAttributeHelper());
3339
0
                pCellAttrs->GetFromEditItemSet( &*pCommonAttrs );
3340
0
            }
3341
0
        }
3342
3343
        // Clear ParaAttribs (including adjustment)
3344
0
        RemoveAdjust();
3345
3346
0
        bool bAttrib = false; // Formatting present?
3347
3348
        //  check if EditObject is needed
3349
0
        if (nParCnt > 1)
3350
0
            bAttrib = true;
3351
0
        else
3352
0
        {
3353
0
            for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && !bAttrib; nId++)
3354
0
            {
3355
0
                SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem );
3356
0
                if (eState == SfxItemState::INVALID)
3357
0
                    bAttrib = true;
3358
0
                else if (eState == SfxItemState::SET)
3359
0
                {
3360
                    // Keep same items in EditEngine as in ScEditAttrTester
3361
0
                    if ( nId == EE_CHAR_ESCAPEMENT || nId == EE_CHAR_PAIRKERNING ||
3362
0
                         nId == EE_CHAR_KERNING || nId == EE_CHAR_XMLATTRIBS )
3363
0
                    {
3364
0
                        if ( *pItem != pEditDefaults->Get(nId) )
3365
0
                            bAttrib = true;
3366
0
                    }
3367
0
                }
3368
0
            }
3369
3370
            // Contains fields?
3371
0
            SfxItemState eFieldState = aOldAttribs.GetItemState( EE_FEATURE_FIELD, false );
3372
0
            if ( eFieldState == SfxItemState::INVALID || eFieldState == SfxItemState::SET )
3373
0
                bAttrib = true;
3374
3375
            // Not converted characters?
3376
0
            SfxItemState eConvState = aOldAttribs.GetItemState( EE_FEATURE_NOTCONV, false );
3377
0
            if ( eConvState == SfxItemState::INVALID || eConvState == SfxItemState::SET )
3378
0
                bAttrib = true;
3379
3380
            // Always recognize formulas as formulas
3381
            // We still need the preceding test due to cell attributes
3382
0
        }
3383
3384
0
        if (bSpellErrors)
3385
0
            mpEditEngine->GetAllMisspellRanges(aMisspellRanges);
3386
3387
0
        if (bMatrix)
3388
0
            bAttrib = false;
3389
3390
0
        if (bAttrib)
3391
0
        {
3392
0
            mpEditEngine->ClearSpellErrors();
3393
0
            pObject = mpEditEngine->CreateTextObject();
3394
0
        }
3395
0
        else if (ScModule::get()->GetAppOptions().GetAutoComplete()) // Adjust Upper/Lower case
3396
0
        {
3397
            // Perform case-matching only when the typed text is partial.
3398
0
            if (pColumnData && aAutoSearch.getLength() < aString.getLength())
3399
0
                aString = getExactMatch(*pColumnData, aString);
3400
0
        }
3401
0
    }
3402
3403
    // Don't rely on ShowRefFrame switching the active view synchronously
3404
    // execute the function directly on the correct view's bindings instead
3405
    // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
3406
0
    ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
3407
3408
0
    if (bFormulaMode)
3409
0
    {
3410
0
        ShowRefFrame();
3411
3412
0
        if (pExecuteSh)
3413
0
        {
3414
0
            pExecuteSh->SetTabNo(aCursorPos.Tab());
3415
0
            pExecuteSh->ActiveGrabFocus();
3416
0
        }
3417
3418
0
        bFormulaMode = false;
3419
0
        pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
3420
0
        ScModule::get()->SetRefInputHdl(nullptr);
3421
0
        if (pInputWin)
3422
0
            pInputWin->SetFormulaMode(false);
3423
0
        UpdateAutoCorrFlag();
3424
0
    }
3425
0
    pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
3426
0
    DeleteRangeFinder();
3427
0
    ResetAutoPar();
3428
3429
0
    bool bOldMod = bModified;
3430
3431
0
    bModified = false;
3432
0
    bSelIsRef = false;
3433
0
    eMode     = SC_INPUT_NONE;
3434
0
    StopInputWinEngine(true);
3435
3436
    // Text input (through number formats) or ApplySelectionPattern modify
3437
    // the cell's attributes, so pLastPattern is no longer valid
3438
0
    pLastPattern = nullptr;
3439
3440
0
    if (bOldMod && !bProtected && !bForget)
3441
0
    {
3442
0
        bool bInsertPreCorrectedString = true;
3443
        // No typographic quotes in formulas
3444
0
        if (aString.startsWith("="))
3445
0
        {
3446
0
            SvxAutoCorrect* pAuto = SvxAutoCorrCfg::Get().GetAutoCorrect();
3447
0
            if ( pAuto )
3448
0
            {
3449
0
                bInsertPreCorrectedString = false;
3450
0
                OUString aReplace(pAuto->GetStartDoubleQuote());
3451
0
                if( aReplace.isEmpty() )
3452
0
                    aReplace = ScGlobal::getLocaleData().getDoubleQuotationMarkStart();
3453
0
                if( aReplace != "\"" )
3454
0
                    aString = aString.replaceAll( aReplace, "\"" );
3455
3456
0
                aReplace = OUString(pAuto->GetEndDoubleQuote());
3457
0
                if( aReplace.isEmpty() )
3458
0
                    aReplace = ScGlobal::getLocaleData().getDoubleQuotationMarkEnd();
3459
0
                if( aReplace != "\"" )
3460
0
                    aString = aString.replaceAll( aReplace, "\"" );
3461
3462
0
                aReplace = OUString(pAuto->GetStartSingleQuote());
3463
0
                if( aReplace.isEmpty() )
3464
0
                    aReplace = ScGlobal::getLocaleData().getQuotationMarkStart();
3465
0
                if( aReplace != "'" )
3466
0
                    aString = aString.replaceAll( aReplace, "'" );
3467
3468
0
                aReplace = OUString(pAuto->GetEndSingleQuote());
3469
0
                if( aReplace.isEmpty() )
3470
0
                    aReplace = ScGlobal::getLocaleData().getQuotationMarkEnd();
3471
0
                if( aReplace != "'" )
3472
0
                    aString = aString.replaceAll( aReplace, "'");
3473
0
            }
3474
0
        }
3475
3476
0
        pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditViewNoPaint ) );
3477
3478
0
        if ( pExecuteSh )
3479
0
        {
3480
0
            SfxBindings& rBindings = pExecuteSh->GetViewFrame().GetBindings();
3481
3482
0
            sal_uInt16 nId = FID_INPUTLINE_ENTER;
3483
0
            if ( nBlockMode == ScEnterMode::BLOCK )
3484
0
                nId = FID_INPUTLINE_BLOCK;
3485
0
            else if ( nBlockMode == ScEnterMode::MATRIX )
3486
0
                nId = FID_INPUTLINE_MATRIX;
3487
3488
0
            const SfxPoolItem* aArgs[2];
3489
0
            aArgs[1] = nullptr;
3490
3491
0
            if ( bInsertPreCorrectedString && aString != aPreAutoCorrectString )
3492
0
            {
3493
0
                ScInputStatusItem aItem(FID_INPUTLINE_STATUS,
3494
0
                                       aCursorPos, aCursorPos, aCursorPos,
3495
0
                                       aPreAutoCorrectString, pObject.get());
3496
0
                aArgs[0] = &aItem;
3497
0
                rBindings.Execute(nId, aArgs);
3498
0
            }
3499
3500
0
            ScInputStatusItem aItemCorrected(FID_INPUTLINE_STATUS,
3501
0
                                             aCursorPos, aCursorPos, aCursorPos,
3502
0
                                             aString, pObject.get());
3503
3504
0
            sc::MisspellRangeResult aMisspellRangeResult;
3505
0
            if ( !aMisspellRanges.empty() )
3506
0
            {
3507
0
                aMisspellRangeResult.meCellLang = mpEditEngine->GetDefaultLanguage();
3508
0
                aMisspellRangeResult.mpRanges = &aMisspellRanges;
3509
0
                aItemCorrected.SetMisspellRanges(aMisspellRangeResult);
3510
0
            }
3511
3512
0
            aArgs[0] = &aItemCorrected;
3513
0
            rBindings.Execute(nId, aArgs);
3514
0
        }
3515
3516
0
        pLastState.reset(); // pLastState still contains the old text
3517
0
    }
3518
0
    else
3519
0
        pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
3520
3521
0
    if ( bOldMod && pExecuteSh && pCellAttrs && !bForget )
3522
0
    {
3523
        // Combine with input?
3524
0
        pExecuteSh->ApplySelectionPattern( *pCellAttrs, true );
3525
0
        pExecuteSh->AdjustBlockHeight();
3526
0
    }
3527
3528
0
    HideTip();
3529
0
    HideTipBelow();
3530
3531
0
    nFormSelStart = nFormSelEnd = 0;
3532
0
    aFormText.clear();
3533
3534
0
    mbEditingExistingContent = false;
3535
0
    bInOwnChange = false;
3536
0
    bInEnterHandler = false;
3537
0
    if (bUpdateLayout)
3538
0
        mpEditEngine->SetUpdateLayout( true );
3539
0
}
3540
3541
void ScInputHandler::CancelHandler()
3542
0
{
3543
0
    bInOwnChange = true; // Also without FormulaMode due to FunctionsAutoPilot
3544
3545
0
    ImplCreateEditEngine();
3546
3547
0
    bModified = false;
3548
0
    mbPartialPrefix = false;
3549
0
    mbEditingExistingContent = false;
3550
3551
    // Don't rely on ShowRefFrame switching the active view synchronously
3552
    // execute the function directly on the correct view's bindings instead
3553
    // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call
3554
0
    ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh;
3555
3556
0
    if (bFormulaMode)
3557
0
    {
3558
0
        ShowRefFrame();
3559
0
        if (pExecuteSh)
3560
0
        {
3561
0
            pExecuteSh->SetTabNo(aCursorPos.Tab());
3562
0
            pExecuteSh->ActiveGrabFocus();
3563
0
        }
3564
0
        bFormulaMode = false;
3565
0
        SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) );
3566
0
        ScModule::get()->SetRefInputHdl(nullptr);
3567
0
        if (pInputWin)
3568
0
            pInputWin->SetFormulaMode(false);
3569
0
        UpdateAutoCorrFlag();
3570
0
    }
3571
0
    pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot
3572
0
    DeleteRangeFinder();
3573
0
    ResetAutoPar();
3574
3575
0
    eMode = SC_INPUT_NONE;
3576
0
    StopInputWinEngine( true );
3577
0
    SCCOL nMaxCol(MAXCOL);
3578
0
    if (pExecuteSh)
3579
0
    {
3580
0
        pExecuteSh->StopEditShell();
3581
0
        nMaxCol = pExecuteSh->GetViewData().GetDocument().MaxCol();
3582
0
    }
3583
3584
0
    aCursorPos.Set(nMaxCol+1,0,0); // Invalid flag
3585
0
    mpEditEngine->SetTextCurrentDefaults(OUString());
3586
3587
0
    if ( !pLastState && pExecuteSh )
3588
0
        pExecuteSh->UpdateInputHandler( true );  // Update status again
3589
0
    else
3590
0
        NotifyChange( pLastState.get(), true );
3591
3592
0
    nFormSelStart = nFormSelEnd = 0;
3593
0
    aFormText.clear();
3594
3595
0
    bInOwnChange = false;
3596
3597
0
    if ( comphelper::LibreOfficeKit::isActive() && pExecuteSh )
3598
0
    {
3599
        // Clear
3600
0
        std::vector<ReferenceMark> aReferenceMarks;
3601
0
        ScInputHandler::SendReferenceMarks( pActiveViewSh, aReferenceMarks );
3602
0
    }
3603
0
}
3604
3605
bool ScInputHandler::IsModalMode( const SfxObjectShell& rDocSh )
3606
0
{
3607
    // References to unnamed document; that doesn't work
3608
0
    return bFormulaMode && pRefViewSh
3609
0
            && pRefViewSh->GetViewData().GetDocument().GetDocumentShell() != &rDocSh
3610
0
            && !rDocSh.HasName();
3611
0
}
3612
3613
void ScInputHandler::AddRefEntry()
3614
0
{
3615
0
    const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
3616
0
    UpdateActiveView();
3617
0
    if (!pTableView && !pTopView)
3618
0
        return;                             // E.g. FillMode
3619
3620
0
    DataChanging();                         // Cannot be new
3621
3622
0
    RemoveSelection();
3623
0
    OUString aText = GetEditText(mpEditEngine.get());
3624
0
    sal_Unicode cLastChar = 0;
3625
0
    sal_Int32 nPos = aText.getLength() - 1;
3626
0
    while (nPos >= 0) //checking space
3627
0
    {
3628
0
        cLastChar = aText[nPos];
3629
0
        if (cLastChar != ' ')
3630
0
            break;
3631
0
        --nPos;
3632
0
    }
3633
3634
0
    bool bAppendSeparator = (cLastChar != '(' && cLastChar != cSep && cLastChar != '=');
3635
0
    if (bAppendSeparator)
3636
0
    {
3637
0
        if (pTableView)
3638
0
            pTableView->InsertText( OUString(cSep) );
3639
0
        if (pTopView)
3640
0
            pTopView->InsertText( OUString(cSep) );
3641
0
    }
3642
3643
0
    DataChanged();
3644
0
}
3645
3646
void ScInputHandler::SetReference( const ScRange& rRef, const ScDocument& rDoc )
3647
0
{
3648
0
    HideTip();
3649
3650
0
    const ScDocument* pThisDoc = nullptr;
3651
0
    if (pRefViewSh)
3652
0
        pThisDoc = &pRefViewSh->GetViewData().GetDocument();
3653
0
    bool bOtherDoc = (pThisDoc != &rDoc);
3654
0
    if (bOtherDoc && !rDoc.GetDocumentShell()->HasName())
3655
0
    {
3656
        // References to unnamed document; that doesn't work
3657
        // SetReference should not be called, then
3658
0
        return;
3659
0
    }
3660
0
    if (!pThisDoc)
3661
0
        pThisDoc = &rDoc;
3662
3663
0
    UpdateActiveView();
3664
0
    if (!pTableView && !pTopView)
3665
0
        return;                             // E.g. FillMode
3666
3667
    // Never overwrite the "="!
3668
0
    EditView* pActiveView = pTopView ? pTopView : pTableView;
3669
0
    ESelection aSel = pActiveView->GetSelection();
3670
0
    aSel.Adjust();
3671
0
    if (aSel.start.nPara == 0 && aSel.start.nIndex == 0)
3672
0
        return;
3673
3674
0
    DataChanging();                         // Cannot be new
3675
3676
    // Turn around selection if backwards.
3677
0
    if (pTableView)
3678
0
    {
3679
0
        ESelection aTabSel = pTableView->GetSelection();
3680
0
        if (aTabSel.start.nIndex > aTabSel.end.nIndex && aTabSel.start.nPara == aTabSel.end.nPara)
3681
0
        {
3682
0
            aTabSel.Adjust();
3683
0
            pTableView->SetSelection(aTabSel);
3684
0
        }
3685
0
    }
3686
0
    if (pTopView)
3687
0
    {
3688
0
        ESelection aTopSel = pTopView->GetSelection();
3689
0
        if (aTopSel.start.nIndex > aTopSel.end.nIndex && aTopSel.start.nPara == aTopSel.end.nPara)
3690
0
        {
3691
0
            aTopSel.Adjust();
3692
0
            pTopView->SetSelection(aTopSel);
3693
0
        }
3694
0
    }
3695
3696
    // Create string from reference, in the syntax of the document being edited.
3697
0
    OUString aRefStr;
3698
0
    const ScAddress::Details aAddrDetails( *pThisDoc, aCursorPos );
3699
0
    if (bOtherDoc)
3700
0
    {
3701
        // Reference to other document
3702
0
        OSL_ENSURE(rRef.aStart.Tab()==rRef.aEnd.Tab(), "nStartTab!=nEndTab");
3703
3704
        // Always 3D and absolute.
3705
0
        OUString aTmp(rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails));
3706
3707
0
        ScDocShell* pObjSh = rDoc.GetDocumentShell();
3708
        // #i75893# convert escaped URL of the document to something user friendly
3709
0
        OUString aFileName = pObjSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
3710
3711
0
        switch(aAddrDetails.eConv)
3712
0
        {
3713
0
             case formula::FormulaGrammar::CONV_XL_A1 :
3714
0
             case formula::FormulaGrammar::CONV_XL_OOX :
3715
0
             case formula::FormulaGrammar::CONV_XL_R1C1 :
3716
0
                         aRefStr = "[\'" + aFileName + "']";
3717
0
                         break;
3718
0
             case formula::FormulaGrammar::CONV_OOO :
3719
0
             default:
3720
0
                         aRefStr = "\'" + aFileName + "'#";
3721
0
                         break;
3722
0
        }
3723
0
        aRefStr += aTmp;
3724
0
    }
3725
0
    else
3726
0
    {
3727
0
        if ( rRef.aStart.Tab() != aCursorPos.Tab() ||
3728
0
             rRef.aStart.Tab() != rRef.aEnd.Tab() )
3729
            // pointer-selected => absolute sheet reference
3730
0
            aRefStr = rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails);
3731
0
        else
3732
0
            aRefStr = rRef.Format(rDoc, ScRefFlags::VALID, aAddrDetails);
3733
0
    }
3734
0
    bool bLOKShowSelect = true;
3735
0
    if(comphelper::LibreOfficeKit::isActive() && pRefViewSh->GetViewData().GetRefTabNo() != pRefViewSh->GetViewData().CurrentTabForData())
3736
0
        bLOKShowSelect = false;
3737
3738
0
    if (pTableView || pTopView)
3739
0
    {
3740
0
        if (pTableView)
3741
0
            pTableView->InsertText( aRefStr, true, bLOKShowSelect );
3742
0
        if (pTopView)
3743
0
            pTopView->InsertText( aRefStr, true, bLOKShowSelect );
3744
3745
0
        DataChanged();
3746
0
    }
3747
3748
0
    bSelIsRef = true;
3749
0
}
3750
3751
void ScInputHandler::InsertFunction( const OUString& rFuncName, bool bAddPar )
3752
0
{
3753
0
    if ( eMode == SC_INPUT_NONE )
3754
0
    {
3755
0
        OSL_FAIL("InsertFunction, not during input mode");
3756
0
        return;
3757
0
    }
3758
3759
0
    UpdateActiveView();
3760
0
    if (!pTableView && !pTopView)
3761
0
        return;                             // E.g. FillMode
3762
3763
0
    DataChanging();                         // Cannot be new
3764
3765
0
    OUString aText = rFuncName;
3766
0
    if (bAddPar)
3767
0
        aText += "()";
3768
3769
0
    if (pTableView)
3770
0
    {
3771
0
        pTableView->InsertText( aText );
3772
0
        if (bAddPar)
3773
0
        {
3774
0
            ESelection aSel = pTableView->GetSelection();
3775
0
            --aSel.start.nIndex;
3776
0
            --aSel.end.nIndex;
3777
0
            pTableView->SetSelection(aSel);
3778
0
        }
3779
0
    }
3780
0
    if (pTopView)
3781
0
    {
3782
0
        pTopView->InsertText( aText );
3783
0
        if (bAddPar)
3784
0
        {
3785
0
            ESelection aSel = pTopView->GetSelection();
3786
0
            --aSel.start.nIndex;
3787
0
            --aSel.end.nIndex;
3788
0
            pTopView->SetSelection(aSel);
3789
0
        }
3790
0
    }
3791
3792
0
    DataChanged();
3793
3794
0
    if (bAddPar)
3795
0
        AutoParAdded();
3796
0
}
3797
3798
void ScInputHandler::ClearText()
3799
0
{
3800
0
    if ( eMode == SC_INPUT_NONE )
3801
0
    {
3802
0
        OSL_FAIL("ClearText, not during input mode");
3803
0
        return;
3804
0
    }
3805
3806
0
    UpdateActiveView();
3807
0
    if (!pTableView && !pTopView)
3808
0
        return;                             // E.g. FillMode
3809
3810
0
    DataChanging();                         // Cannot be new
3811
3812
0
    if (pTableView)
3813
0
    {
3814
0
        pTableView->getEditEngine().SetText( u""_ustr );
3815
0
        pTableView->SetSelection(ESelection());
3816
0
    }
3817
0
    if (pTopView)
3818
0
    {
3819
0
        pTopView->getEditEngine().SetText( u""_ustr );
3820
0
        pTopView->SetSelection(ESelection());
3821
0
    }
3822
3823
0
    DataChanged();
3824
0
}
3825
3826
bool ScInputHandler::KeyInput( const KeyEvent& rKEvt, bool bStartEdit /* = false */ )
3827
0
{
3828
0
    vcl::KeyCode aCode = rKEvt.GetKeyCode();
3829
0
    sal_uInt16 nModi  = aCode.GetModifier();
3830
0
    bool bShift   = aCode.IsShift();
3831
0
    bool bControl = aCode.IsMod1();
3832
0
    bool bAlt     = aCode.IsMod2();
3833
0
    sal_uInt16 nCode  = aCode.GetCode();
3834
0
    sal_Unicode nChar = rKEvt.GetCharCode();
3835
3836
0
    if (bAlt && !bControl && nCode != KEY_RETURN)
3837
        // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not.
3838
0
        return false;
3839
3840
    // There is a partial autocomplete suggestion.
3841
    // Allow its completion with right arrow key (without modifiers).
3842
0
    if (mbPartialPrefix && nCode == KEY_RIGHT && !bControl && !bShift && !bAlt &&
3843
0
        (pTopView || pTableView))
3844
0
    {
3845
0
        if (pTopView)
3846
0
            pTopView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH));
3847
0
        if (pTableView)
3848
0
            pTableView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH));
3849
3850
0
        mbPartialPrefix = false;
3851
3852
        // Indicate that this event has been consumed and ScTabViewShell should not act on this.
3853
0
        return true;
3854
0
    }
3855
3856
0
    if (!bControl && nCode == KEY_TAB)
3857
0
    {
3858
        // Normal TAB moves the cursor right.
3859
0
        EnterHandler();
3860
3861
0
        if (pActiveViewSh)
3862
0
            pActiveViewSh->FindNextUnprot( bShift, true );
3863
3864
0
        ScModule* pScMod = ScModule::get();
3865
0
        const ScInputOptions& rOpt = pScMod->GetInputOptions();
3866
0
        const bool bKit = comphelper::LibreOfficeKit::isActive();
3867
3868
0
        if ( (rOpt.GetMoveKeepEdit() && !bKit)
3869
0
             || (pActiveViewSh && pActiveViewSh->GetMoveKeepEdit() && bKit) )
3870
0
            pScMod->SetInputMode( SC_INPUT_TABLE );
3871
3872
0
        return true;
3873
0
    }
3874
3875
0
    bool bInputLine = ( eMode==SC_INPUT_TOP );
3876
3877
0
    bool bUsed = false;
3878
0
    bool bSkip = false;
3879
0
    bool bDoEnter = false;
3880
3881
0
    switch ( nCode )
3882
0
    {
3883
0
        case KEY_RETURN:
3884
            // New line when in the input line and Shift/Ctrl-Enter is pressed,
3885
            // or when in a cell and Ctrl-Enter is pressed.
3886
0
            if ((pInputWin && bInputLine && bControl != bShift) || (!bInputLine && bControl && !bShift))
3887
0
            {
3888
0
                bDoEnter = true;
3889
0
            }
3890
0
            else if (nModi == 0 && nTipVisible && pFormulaData && miAutoPosFormula != pFormulaData->end())
3891
0
            {
3892
0
                PasteFunctionData();
3893
0
                bUsed = true;
3894
0
            }
3895
0
            else if ( nModi == 0 && nTipVisible && !aManualTip.isEmpty() )
3896
0
            {
3897
0
                PasteManualTip();
3898
0
                bUsed = true;
3899
0
            }
3900
0
            else
3901
0
            {
3902
0
                ScEnterMode nMode = ScEnterMode::NORMAL;
3903
0
                if ( bShift && bControl )
3904
0
                    nMode = ScEnterMode::MATRIX;
3905
0
                else if ( bAlt )
3906
0
                    nMode = ScEnterMode::BLOCK;
3907
0
                EnterHandler( nMode );
3908
3909
0
                if (pActiveViewSh)
3910
0
                    pActiveViewSh->MoveCursorEnter( bShift && !bControl );
3911
3912
0
                ScModule* pScMod = ScModule::get();
3913
0
                const ScInputOptions& rOpt = pScMod->GetInputOptions();
3914
0
                const bool bKit = comphelper::LibreOfficeKit::isActive();
3915
3916
0
                if ( (rOpt.GetMoveKeepEdit() && !bKit)
3917
0
                    || (pActiveViewSh && pActiveViewSh->GetMoveKeepEdit() && bKit) )
3918
0
                    pScMod->SetInputMode( SC_INPUT_TABLE );
3919
3920
0
                bUsed = true;
3921
0
            }
3922
0
            break;
3923
0
        case KEY_TAB:
3924
0
            if (bControl && !bAlt)
3925
0
            {
3926
0
                if (pFormulaData && nTipVisible && miAutoPosFormula != pFormulaData->end())
3927
0
                {
3928
                    // Iterate
3929
0
                    NextFormulaEntry( bShift );
3930
0
                    bUsed = true;
3931
0
                }
3932
0
                else if (pColumnData && bUseTab)
3933
0
                {
3934
                    // Iterate through AutoInput entries
3935
0
                    NextAutoEntry( bShift );
3936
0
                    bUsed = true;
3937
0
                }
3938
0
            }
3939
0
            break;
3940
0
        case KEY_ESCAPE:
3941
0
            if ( nTipVisible )
3942
0
            {
3943
0
                HideTip();
3944
0
                bUsed = true;
3945
0
            }
3946
0
            else if( nTipVisibleSec )
3947
0
            {
3948
0
                HideTipBelow();
3949
0
                bUsed = true;
3950
0
            }
3951
0
            else if (eMode != SC_INPUT_NONE)
3952
0
            {
3953
0
                CancelHandler();
3954
0
                bUsed = true;
3955
0
            }
3956
0
            else
3957
0
                bSkip = true;
3958
0
            break;
3959
0
    }
3960
3961
    // Only execute cursor keys if already in EditMode
3962
    // E.g. due to Shift-Ctrl-PageDn (not defined as an accelerator)
3963
0
    bool bCursorKey = EditEngine::DoesKeyMoveCursor(rKEvt);
3964
0
    bool bInsKey = ( nCode == KEY_INSERT && !nModi ); // Treat Insert like Cursorkeys
3965
0
    if ( !bUsed && !bSkip && ( bDoEnter || EditEngine::DoesKeyChangeText(rKEvt) ||
3966
0
                    ( eMode != SC_INPUT_NONE && ( bCursorKey || bInsKey ) ) ) )
3967
0
    {
3968
0
        HideTip();
3969
0
        HideTipBelow();
3970
3971
0
        if (bSelIsRef)
3972
0
        {
3973
0
            RemoveSelection();
3974
0
            bSelIsRef = false;
3975
0
        }
3976
3977
0
        UpdateActiveView();
3978
0
        bool bNewView = DataChanging( nChar );
3979
3980
0
        if (bProtected || (pActiveViewSh && pActiveViewSh->GetViewShell() && pActiveViewSh->GetViewShell()->IsLokReadOnlyView())) // Protected cell?
3981
0
            bUsed = true;                           // Don't forward KeyEvent
3982
0
        else                                        // Changes allowed
3983
0
        {
3984
0
            if (bNewView )                          // Create anew
3985
0
            {
3986
0
                if (pActiveViewSh)
3987
0
                    pActiveViewSh->GetViewData().GetDocShell()->PostEditView(*mpEditEngine, aCursorPos);
3988
0
                UpdateActiveView();
3989
0
                if (eMode==SC_INPUT_NONE)
3990
0
                    if (pTableView || pTopView)
3991
0
                    {
3992
0
                        OUString aStrLoP;
3993
3994
0
                        if (bStartEdit && nCellPercentFormatDecSep != 0 &&
3995
0
                                ((nChar >= '0' && nChar <= '9') || nChar == '-' || nChar == nCellPercentFormatDecSep))
3996
0
                        {
3997
0
                            aStrLoP = "%";
3998
0
                        }
3999
4000
0
                        if (pTableView)
4001
0
                        {
4002
0
                            pTableView->getEditEngine().SetText( aStrLoP );
4003
0
                            if ( !aStrLoP.isEmpty() )
4004
0
                                pTableView->SetSelection(ESelection());   // before the '%'
4005
4006
                            // Don't call SetSelection if the string is empty anyway,
4007
                            // to avoid breaking the bInitial handling in ScViewData::EditGrowY
4008
0
                        }
4009
0
                        if (pTopView)
4010
0
                        {
4011
0
                            pTopView->getEditEngine().SetText( aStrLoP );
4012
0
                            if ( !aStrLoP.isEmpty() )
4013
0
                                pTopView->SetSelection(ESelection());     // before the '%'
4014
0
                        }
4015
0
                    }
4016
0
                SyncViews();
4017
0
            }
4018
4019
0
            if (pTableView || pTopView)
4020
0
            {
4021
0
                if (bDoEnter)
4022
0
                {
4023
0
                    if (pTableView)
4024
0
                        if( pTableView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) )
4025
0
                            bUsed = true;
4026
0
                    if (pTopView)
4027
0
                        if( pTopView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) )
4028
0
                            bUsed = true;
4029
0
                }
4030
0
                else if ( nAutoPar && nChar == ')' && CursorAtClosingPar() )
4031
0
                {
4032
0
                    SkipClosingPar();
4033
0
                    bUsed = true;
4034
0
                }
4035
0
                else
4036
0
                {
4037
0
                    if (pTableView)
4038
0
                    {
4039
0
                        if (pTopView)
4040
0
                            pTableView->SetControlWord(pTableView->GetControlWord() | EVControlBits::SINGLELINEPASTE);
4041
4042
0
                        vcl::Window* pFrameWin = pActiveViewSh ? pActiveViewSh->GetFrameWin() : nullptr;
4043
0
                        if ( pTableView->PostKeyEvent( rKEvt, pFrameWin ) )
4044
0
                            bUsed = true;
4045
4046
0
                        pTableView->SetControlWord(pTableView->GetControlWord() & ~EVControlBits::SINGLELINEPASTE);
4047
0
                    }
4048
0
                    if (pTopView)
4049
0
                    {
4050
0
                        if ( bUsed && rKEvt.GetKeyCode().GetFunction() == KeyFuncType::CUT )
4051
0
                            pTopView->DeleteSelected();
4052
0
                        else if ( pTopView->PostKeyEvent( rKEvt ) )
4053
0
                            bUsed = true;
4054
0
                    }
4055
0
                }
4056
4057
                // AutoInput:
4058
0
                if (bUsed && ScModule::get()->GetAppOptions().GetAutoComplete())
4059
0
                {
4060
0
                    bUseTab = false;
4061
0
                    if (pFormulaData)
4062
0
                        miAutoPosFormula = pFormulaData->end();     // do not search further
4063
0
                    if (pColumnData)
4064
0
                        miAutoPosColumn = pColumnData->end();
4065
4066
0
                    KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
4067
0
                    if ( nChar && nChar != 8 && nChar != 127 &&     // no 'backspace', no 'delete'
4068
0
                         KeyFuncType::CUT != eFunc)                      // and no 'CTRL-X'
4069
0
                    {
4070
0
                        if (bFormulaMode)
4071
0
                            UseFormulaData();
4072
0
                        else
4073
0
                            UseColData();
4074
0
                    }
4075
0
                }
4076
4077
                // When the selection is changed manually or an opening parenthesis
4078
                // is typed, stop overwriting parentheses
4079
0
                if ( bUsed && nChar == '(' )
4080
0
                    ResetAutoPar();
4081
4082
0
                if ( KEY_INSERT == nCode )
4083
0
                {
4084
0
                    SfxViewFrame* pViewFrm = SfxViewFrame::Current();
4085
0
                    if (pViewFrm)
4086
0
                        pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT );
4087
0
                }
4088
0
                if( bUsed && bFormulaMode && ( bCursorKey || bInsKey || nCode == KEY_DELETE || nCode == KEY_BACKSPACE ) )
4089
0
                {
4090
0
                    ShowTipCursor();
4091
0
                }
4092
0
                if( bUsed && bFormulaMode && nCode == KEY_BACKSPACE )
4093
0
                {
4094
0
                    UseFormulaData();
4095
0
                }
4096
4097
0
            }
4098
4099
            // #i114511# don't count cursor keys as modification
4100
0
            bool bSetModified = !bCursorKey;
4101
            // tdf#81913 - don't delete range finder since cursor keys don't count as modifications
4102
0
            bInRangeUpdate = bCursorKey;
4103
0
            DataChanged(false, bSetModified); // also calls UpdateParenthesis()
4104
0
            bInRangeUpdate = false;
4105
4106
            // In the LOK case, we want to set the document modified state
4107
            // right away at the start of the edit, so that the content is
4108
            // saved even when the user leaves the document before hitting
4109
            // Enter
4110
0
            if (comphelper::LibreOfficeKit::isActive() && bSetModified && pActiveViewSh && !pActiveViewSh->GetViewData().GetDocShell()->IsModified())
4111
0
                pActiveViewSh->GetViewData().GetDocShell()->SetModified();
4112
4113
0
            InvalidateAttribs();        //! in DataChanged?
4114
0
        }
4115
0
    }
4116
4117
0
    if (pTopView && eMode != SC_INPUT_NONE)
4118
0
        SyncViews();
4119
4120
0
    return bUsed;
4121
0
}
4122
4123
OUString ScInputHandler::GetSurroundingText()
4124
0
{
4125
0
    if (eMode != SC_INPUT_NONE)
4126
0
    {
4127
0
        UpdateActiveView();
4128
0
        if (pTableView || pTopView)
4129
0
        {
4130
0
            if (pTableView)
4131
0
                return pTableView->GetSurroundingText();
4132
0
            else if (pTopView)                      // call only once
4133
0
                return pTopView->GetSurroundingText();
4134
0
        }
4135
0
    }
4136
0
    return OUString();
4137
0
}
4138
4139
Selection ScInputHandler::GetSurroundingTextSelection()
4140
0
{
4141
0
    if (eMode != SC_INPUT_NONE)
4142
0
    {
4143
0
        UpdateActiveView();
4144
0
        if (pTableView || pTopView)
4145
0
        {
4146
0
            if (pTableView)
4147
0
                return pTableView->GetSurroundingTextSelection();
4148
0
            else if (pTopView)                      // call only once
4149
0
                return pTopView->GetSurroundingTextSelection();
4150
0
        }
4151
0
    }
4152
0
    return Selection(0, 0);
4153
0
}
4154
4155
bool ScInputHandler::DeleteSurroundingText(const Selection& rSelection)
4156
0
{
4157
0
    if (eMode != SC_INPUT_NONE)
4158
0
    {
4159
0
        UpdateActiveView();
4160
0
        if (pTableView || pTopView)
4161
0
        {
4162
0
            if (pTableView)
4163
0
                return pTableView->DeleteSurroundingText(rSelection);
4164
0
            else if (pTopView)                      // call only once
4165
0
                return pTopView->DeleteSurroundingText(rSelection);
4166
0
        }
4167
0
    }
4168
0
    return false;
4169
0
}
4170
4171
void ScInputHandler::InputCommand( const CommandEvent& rCEvt )
4172
0
{
4173
0
    if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
4174
0
    {
4175
        // For CommandEventId::CursorPos, do as little as possible, because
4176
        // with remote VCL, even a ShowCursor will generate another event.
4177
0
        if ( eMode != SC_INPUT_NONE )
4178
0
        {
4179
0
            UpdateActiveView();
4180
0
            if (pTableView || pTopView)
4181
0
            {
4182
0
                if (pTableView)
4183
0
                    pTableView->Command( rCEvt );
4184
0
                else if (pTopView)                      // call only once
4185
0
                    pTopView->Command( rCEvt );
4186
0
            }
4187
0
        }
4188
0
    }
4189
0
    else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
4190
0
    {
4191
0
        if ( eMode != SC_INPUT_NONE )
4192
0
        {
4193
0
            UpdateActiveView();
4194
0
            if (pTableView || pTopView)
4195
0
            {
4196
0
                if (pTableView)
4197
0
                    pTableView->Command( rCEvt );
4198
0
                else if (pTopView)                      // call only once
4199
0
                    pTopView->Command( rCEvt );
4200
0
            }
4201
0
        }
4202
0
    }
4203
0
    else
4204
0
    {
4205
0
        HideTip();
4206
0
        HideTipBelow();
4207
4208
0
        if ( bSelIsRef )
4209
0
        {
4210
0
            RemoveSelection();
4211
0
            bSelIsRef = false;
4212
0
        }
4213
4214
0
        UpdateActiveView();
4215
0
        bool bNewView = DataChanging( 0, true );
4216
4217
0
        if (!bProtected && pActiveViewSh && !(pActiveViewSh->GetViewShell() && pActiveViewSh->GetViewShell()->IsLokReadOnlyView())) // changes allowed
4218
0
        {
4219
0
            if (bNewView)                           // create new edit view
4220
0
            {
4221
0
                pActiveViewSh->GetViewData().GetDocShell()->PostEditView(*mpEditEngine, aCursorPos);
4222
0
                UpdateActiveView();
4223
0
                if (eMode==SC_INPUT_NONE)
4224
0
                    if (pTableView || pTopView)
4225
0
                    {
4226
0
                        if (pTableView)
4227
0
                        {
4228
0
                            pTableView->getEditEngine().SetText( u""_ustr );
4229
0
                            pTableView->SetSelection(ESelection());
4230
0
                        }
4231
0
                        if (pTopView)
4232
0
                        {
4233
0
                            pTopView->getEditEngine().SetText( u""_ustr );
4234
0
                            pTopView->SetSelection(ESelection());
4235
0
                        }
4236
0
                    }
4237
0
                SyncViews();
4238
0
            }
4239
4240
0
            if (pTableView || pTopView)
4241
0
            {
4242
0
                if (pTableView)
4243
0
                    pTableView->Command( rCEvt );
4244
0
                if (pTopView)
4245
0
                    pTopView->Command( rCEvt );
4246
4247
0
                if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
4248
0
                {
4249
                    //  AutoInput after ext text input
4250
4251
0
                    if (pFormulaData)
4252
0
                        miAutoPosFormula = pFormulaData->end();
4253
0
                    if (pColumnData)
4254
0
                        miAutoPosColumn = pColumnData->end();
4255
4256
0
                    if (bFormulaMode)
4257
0
                        UseFormulaData();
4258
0
                    else
4259
0
                        UseColData();
4260
0
                }
4261
0
            }
4262
4263
0
            DataChanged();              //  calls UpdateParenthesis()
4264
0
            InvalidateAttribs();        //! in DataChanged ?
4265
0
        }
4266
4267
0
        if (pTopView && eMode != SC_INPUT_NONE)
4268
0
            SyncViews();
4269
0
    }
4270
0
}
4271
4272
static ScInputHdlState* getLastState(const ScInputHdlState* pState)
4273
0
{
4274
0
    if (!pState)
4275
0
        return nullptr;
4276
0
    return new ScInputHdlState(*pState);
4277
0
}
4278
4279
void ScInputHandler::NotifyChange( const ScInputHdlState* pState,
4280
                                   bool bForce, ScTabViewShell* pSourceSh,
4281
                                   bool bStopEditing)
4282
0
{
4283
    // If the call originates from a macro call in the EnterHandler,
4284
    // return immediately and don't mess up the status
4285
0
    if (bInEnterHandler)
4286
0
        return;
4287
4288
0
    bool bRepeat = (pState == pLastState.get());
4289
0
    if (!bRepeat && pState && pLastState)
4290
0
        bRepeat = (*pState == *pLastState);
4291
0
    if (bRepeat && !bForce)
4292
0
        return;
4293
4294
0
    bInOwnChange = true;                // disable ModifyHdl (reset below)
4295
4296
0
    if ( pState && !pLastState )        // Enable again
4297
0
        bForce = true;
4298
4299
0
    bool bHadObject = pLastState && pLastState->GetEditData();
4300
4301
    //! Before EditEngine gets eventually created (so it gets the right pools)
4302
0
    if ( pSourceSh )
4303
0
        pActiveViewSh = pSourceSh;
4304
0
    else
4305
0
        pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() );
4306
4307
0
    if (pActiveViewSh)
4308
0
        ImplCreateEditEngine();
4309
4310
0
    if ( pState != pLastState.get() )
4311
0
    {
4312
0
        pLastState.reset(getLastState(pState));
4313
0
    }
4314
4315
0
    if ( pState && pActiveViewSh )
4316
0
    {
4317
0
        ScModule* pScMod = ScModule::get();
4318
4319
0
        ScTabViewShell* pScTabViewShell = dynamic_cast<ScTabViewShell*>(pScMod->GetViewShell());
4320
4321
        // Also take foreign reference input into account here (e.g. FunctionsAutoPilot),
4322
        // FormEditData, if we're switching from Help to Calc:
4323
0
        if ( !bFormulaMode && !pScMod->IsFormulaMode() &&
4324
0
             ( !pScTabViewShell || !pScTabViewShell->GetFormEditData() ) )
4325
0
        {
4326
0
            bool bIgnore = false;
4327
0
            if ( bModified )
4328
0
            {
4329
0
                if (pState->GetPos() != aCursorPos)
4330
0
                {
4331
0
                    if (!bProtected)
4332
0
                        EnterHandler();
4333
0
                }
4334
0
                else
4335
0
                    bIgnore = true;
4336
0
            }
4337
4338
0
            if ( !bIgnore )
4339
0
            {
4340
0
                const ScAddress&        rSPos   = pState->GetStartPos();
4341
0
                const ScAddress&        rEPos   = pState->GetEndPos();
4342
0
                const EditTextObject*   pData   = pState->GetEditData();
4343
0
                OUString aString = pState->GetString();
4344
0
                bool bTxtMod = false;
4345
0
                ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell();
4346
0
                ScDocument& rDoc = pDocSh->GetDocument();
4347
4348
0
                aCursorPos  = pState->GetPos();
4349
4350
0
                if ( pData )
4351
0
                    bTxtMod = true;
4352
0
                else if ( bHadObject )
4353
0
                    bTxtMod = true;
4354
0
                else if ( bTextValid )
4355
0
                    bTxtMod = ( aString != aCurrentText );
4356
0
                else
4357
0
                    bTxtMod = ( aString != GetEditText(mpEditEngine.get()) );
4358
4359
0
                if ( bTxtMod || bForce )
4360
0
                {
4361
0
                    if (pData)
4362
0
                    {
4363
0
                        mpEditEngine->SetTextCurrentDefaults( *pData );
4364
0
                        if (pInputWin)
4365
0
                            aString = ScEditUtil::GetMultilineString(*mpEditEngine);
4366
0
                        else
4367
0
                            aString = GetEditText(mpEditEngine.get());
4368
0
                        lcl_RemoveTabs(aString);
4369
0
                        bTextValid = false;
4370
0
                        aCurrentText.clear();
4371
0
                    }
4372
0
                    else
4373
0
                    {
4374
0
                        aCurrentText = aString;
4375
0
                        bTextValid = true;              //! To begin with remember as a string
4376
0
                    }
4377
4378
0
                    const bool bUpdateKit = comphelper::LibreOfficeKit::isActive() && pActiveViewSh;
4379
4380
0
                    if (pInputWin)
4381
0
                    {
4382
                        // If we will end up updating LoKit after this, we can skip it here
4383
0
                        pInputWin->SetTextString(aString, !bUpdateKit);
4384
0
                    }
4385
4386
0
                    if (bUpdateKit)
4387
0
                    {
4388
0
                        UpdateActiveView();
4389
0
                        EditView* pActiveView = pTopView ? pTopView : pTableView;
4390
0
                        ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection();
4391
4392
                        // if we switched content completely - don't send huge numbers
4393
0
                        if (aSel.start.nPara == EE_PARA_MAX)
4394
0
                            aSel.start.nPara = 0;
4395
4396
0
                        if (aSel.end.nPara == EE_PARA_MAX)
4397
0
                            aSel.end.nPara = 0;
4398
4399
0
                        pActiveViewSh->LOKSendFormulabarUpdate(pActiveView, aString, aSel);
4400
0
                        pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aString.toUtf8());
4401
0
                    }
4402
0
                }
4403
4404
0
                if ( pInputWin || comphelper::LibreOfficeKit::isActive())                        // Named range input
4405
0
                {
4406
0
                    OUString aPosStr;
4407
0
                    bool bSheetLocal = false;
4408
0
                    const ScAddress::Details aAddrDetails( rDoc, aCursorPos );
4409
4410
                    // Is the range a name?
4411
                    //! Find by Timer?
4412
0
                    if ( pActiveViewSh )
4413
0
                        pActiveViewSh->GetViewData().GetDocument().
4414
0
                            GetRangeAtBlock( ScRange( rSPos, rEPos ), aPosStr, &bSheetLocal);
4415
4416
0
                    if ( aPosStr.isEmpty() )           // Not a name -> format
4417
0
                    {
4418
0
                        ScRefFlags nFlags = ScRefFlags::ZERO;
4419
0
                        if( aAddrDetails.eConv == formula::FormulaGrammar::CONV_XL_R1C1 )
4420
0
                            nFlags |= ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS;
4421
0
                        if ( rSPos != rEPos )
4422
0
                        {
4423
0
                            ScRange r(rSPos, rEPos);
4424
0
                            applyStartToEndFlags(nFlags);
4425
0
                            aPosStr = r.Format(rDoc, ScRefFlags::VALID | nFlags, aAddrDetails);
4426
0
                        }
4427
0
                        else
4428
0
                            aPosStr = aCursorPos.Format(ScRefFlags::VALID | nFlags, &rDoc, aAddrDetails);
4429
0
                    }
4430
0
                    else if (bSheetLocal)
4431
0
                    {
4432
0
                        OUString aName;
4433
0
                        if (rDoc.GetName( rSPos.Tab(), aName))
4434
0
                            aPosStr = ScPosWnd::createLocalRangeName( aPosStr, aName);
4435
0
                    }
4436
4437
0
                    if (pInputWin)
4438
0
                    {
4439
0
                        pInputWin->SetPosString(aPosStr);
4440
0
                        pInputWin->SetSumAssignMode();
4441
0
                    }
4442
4443
0
                    if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh)
4444
0
                        pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_ADDRESS, aPosStr.toUtf8());
4445
0
                }
4446
4447
0
                if (bStopEditing) {
4448
0
                    SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScKillEditView ) );
4449
4450
                    //  As long as the content is not edited, turn off online spelling.
4451
                    //  Online spelling is turned back on in StartTable, after setting
4452
                    //  the right language from cell attributes.
4453
4454
0
                    EEControlBits nCntrl = mpEditEngine->GetControlWord();
4455
0
                    if ( nCntrl & EEControlBits::ONLINESPELLING )
4456
0
                        mpEditEngine->SetControlWord( nCntrl & ~EEControlBits::ONLINESPELLING );
4457
0
                }
4458
4459
0
                bModified = false;
4460
0
                bSelIsRef = false;
4461
0
                bProtected = false;
4462
0
                bCommandErrorShown = false;
4463
0
            }
4464
0
        }
4465
4466
0
        if ( pInputWin)
4467
0
        {
4468
            // Do not enable if RefDialog is open
4469
0
            if(!pScMod->IsFormulaMode()&& !pScMod->IsRefDialogOpen())
4470
0
            {
4471
0
                if ( !pInputWin->IsEnabled())
4472
0
                {
4473
0
                    pDelayTimer->Stop();
4474
0
                    pInputWin->Enable();
4475
0
                }
4476
0
            }
4477
0
            else if(pScMod->IsRefDialogOpen())
4478
0
            {   // Because every document has its own InputWin,
4479
                // we should start Timer again, because the input line may
4480
                // still be active
4481
0
                if ( !pDelayTimer->IsActive() )
4482
0
                    pDelayTimer->Start();
4483
0
            }
4484
0
        }
4485
0
    }
4486
0
    else // !pState || !pActiveViewSh
4487
0
    {
4488
0
        if ( !pDelayTimer->IsActive() )
4489
0
            pDelayTimer->Start();
4490
0
    }
4491
4492
    // Don't hide function tooltip in LOK, a remote user might be using tip.
4493
0
    if (bStopEditing)
4494
0
        HideTip();
4495
0
    HideTipBelow();
4496
0
    bInOwnChange = false;
4497
0
}
4498
4499
void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust )
4500
0
{
4501
0
    eAttrAdjust = eJust;
4502
0
    UpdateAdjust( 0 );
4503
0
}
4504
4505
void ScInputHandler::ResetDelayTimer()
4506
0
{
4507
0
    if( pDelayTimer->IsActive() )
4508
0
    {
4509
0
        pDelayTimer->Stop();
4510
0
        if ( pInputWin )
4511
0
            pInputWin->Enable();
4512
0
    }
4513
0
}
4514
4515
IMPL_LINK_NOARG( ScInputHandler, DelayTimer, Timer*, void )
4516
0
{
4517
0
    if (!(nullptr == pLastState || ScModule::get()->IsFormulaMode() || ScModule::get()->IsRefDialogOpen()))
4518
0
        return;
4519
4520
    //! New method at ScModule to query if function autopilot is open
4521
0
    SfxViewFrame* pViewFrm = SfxViewFrame::Current();
4522
0
    if ( pViewFrm && pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) )
4523
0
    {
4524
0
        if ( pInputWin)
4525
0
        {
4526
0
            pInputWin->EnableButtons( false );
4527
0
            pInputWin->Disable();
4528
0
        }
4529
0
    }
4530
0
    else if ( !bFormulaMode ) // Keep formula e.g. for help
4531
0
    {
4532
0
        bInOwnChange = true; // disable ModifyHdl (reset below)
4533
4534
0
        pActiveViewSh = nullptr;
4535
0
        mpEditEngine->SetTextCurrentDefaults( OUString() );
4536
0
        if ( pInputWin )
4537
0
        {
4538
0
            pInputWin->SetPosString( OUString() );
4539
0
            pInputWin->SetTextString(OUString(), true);
4540
0
            pInputWin->Disable();
4541
0
        }
4542
4543
0
        bInOwnChange = false;
4544
0
    }
4545
0
}
4546
4547
void ScInputHandler::InputSelection( const EditView* pView )
4548
0
{
4549
0
    SyncViews( pView );
4550
0
    ShowTipCursor();
4551
0
    UpdateParenthesis(); // Selection changed -> update parentheses highlighting
4552
4553
    // When the selection is changed manually, stop overwriting parentheses
4554
0
    ResetAutoPar();
4555
4556
0
    if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh)
4557
0
    {
4558
0
        EditView* pActiveView = pTopView ? pTopView : pTableView;
4559
0
        ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection();
4560
0
        pActiveViewSh->LOKSendFormulabarUpdate(pActiveView, GetEditString(), aSel);
4561
0
    }
4562
0
}
4563
4564
void ScInputHandler::InputChanged( const EditView* pView, bool bFromNotify )
4565
0
{
4566
0
    if ( !pView )
4567
0
        return;
4568
4569
0
    UpdateActiveView();
4570
4571
    // #i20282# DataChanged needs to know if this is from the input line's modify handler
4572
0
    bool bFromTopNotify = ( bFromNotify && pView == pTopView );
4573
4574
0
    bool bNewView = DataChanging();                     //FIXME: Is this at all possible?
4575
0
    aCurrentText = pView->getEditEngine().GetText();   // Also remember the string
4576
0
    mpEditEngine->SetTextCurrentDefaults( aCurrentText );
4577
0
    DataChanged( bFromTopNotify );
4578
0
    bTextValid = true; // Is set to false in DataChanged
4579
4580
0
    if ( pActiveViewSh )
4581
0
    {
4582
0
        ScViewData& rViewData = pActiveViewSh->GetViewData();
4583
0
        if ( bNewView )
4584
0
            rViewData.GetDocShell()->PostEditView(*mpEditEngine, aCursorPos);
4585
4586
0
        rViewData.EditGrowY();
4587
0
        rViewData.EditGrowX();
4588
0
    }
4589
4590
0
    SyncViews( pView );
4591
0
}
4592
4593
const OUString& ScInputHandler::GetEditString()
4594
0
{
4595
0
    if (mpEditEngine)
4596
0
    {
4597
0
        aCurrentText = mpEditEngine->GetText(); // Always new from Engine
4598
0
        bTextValid = true;
4599
0
    }
4600
4601
0
    return aCurrentText;
4602
0
}
4603
4604
Size ScInputHandler::GetTextSize()
4605
0
{
4606
0
    Size aSize;
4607
0
    if ( mpEditEngine )
4608
0
        aSize = Size( mpEditEngine->CalcTextWidth(), mpEditEngine->GetTextHeight() );
4609
4610
0
    return aSize;
4611
0
}
4612
4613
bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter& rDestEngine )
4614
0
{
4615
0
    bool bRet = false;
4616
0
    if (mpEditEngine)
4617
0
    {
4618
        // Contains field?
4619
0
        SfxItemSet aSet = mpEditEngine->GetAttribs(ESelection::All());
4620
0
        SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
4621
0
        if ( eFieldState == SfxItemState::INVALID || eFieldState == SfxItemState::SET )
4622
0
        {
4623
            // Copy content
4624
0
            std::unique_ptr<EditTextObject> pObj = mpEditEngine->CreateTextObject();
4625
0
            rDestEngine.SetTextCurrentDefaults(*pObj);
4626
0
            pObj.reset();
4627
4628
0
            sal_Int32 nParCnt = mpEditEngine->GetParagraphCount();
4629
            // Delete attributes
4630
0
            for (sal_Int32 i=0; i<nParCnt; i++)
4631
0
                rDestEngine.RemoveCharAttribs( i );
4632
4633
            // Combine paragraphs
4634
0
            while ( nParCnt > 1 )
4635
0
            {
4636
0
                sal_Int32 nLen = rDestEngine.GetTextLen( 0 );
4637
0
                ESelection aSel( 0,nLen, 1,0 );
4638
0
                rDestEngine.QuickInsertText( OUString(' '), aSel ); // Replace line break with space
4639
0
                --nParCnt;
4640
0
            }
4641
4642
0
            bRet = true;
4643
0
        }
4644
0
    }
4645
0
    return bRet;
4646
0
}
4647
4648
/**
4649
 * Methods for FunctionAutoPilot:
4650
 * InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr
4651
 */
4652
void ScInputHandler::InputGetSelection( sal_Int32& rStart, sal_Int32& rEnd )
4653
0
{
4654
0
    rStart = nFormSelStart;
4655
0
    rEnd = nFormSelEnd;
4656
0
}
4657
4658
EditView* ScInputHandler::GetFuncEditView()
4659
0
{
4660
0
    UpdateActiveView(); // Due to pTableView
4661
4662
0
    EditView* pView = nullptr;
4663
0
    if ( pInputWin )
4664
0
    {
4665
0
        pInputWin->MakeDialogEditView();
4666
0
        pView = pInputWin->GetEditView();
4667
0
    }
4668
0
    else
4669
0
    {
4670
0
        if ( eMode != SC_INPUT_TABLE )
4671
0
        {
4672
0
            bCreatingFuncView = true; // Don't display RangeFinder
4673
0
            SetMode( SC_INPUT_TABLE );
4674
0
            bCreatingFuncView = false;
4675
0
            if ( pTableView )
4676
0
                pTableView->getEditEngine().SetText( OUString() );
4677
0
        }
4678
0
        pView = pTableView;
4679
0
    }
4680
4681
0
    return pView;
4682
0
}
4683
4684
void ScInputHandler::InputSetSelection( sal_Int32 nStart, sal_Int32 nEnd )
4685
0
{
4686
0
    if ( nStart <= nEnd )
4687
0
    {
4688
0
        nFormSelStart = nStart;
4689
0
        nFormSelEnd = nEnd;
4690
0
    }
4691
0
    else
4692
0
    {
4693
0
        nFormSelEnd = nStart;
4694
0
        nFormSelStart = nEnd;
4695
0
    }
4696
4697
0
    EditView* pView = GetFuncEditView();
4698
0
    if (pView)
4699
0
        pView->SetSelection( ESelection(0,nStart, 0,nEnd) );
4700
4701
0
    bModified = true;
4702
0
}
4703
4704
void ScInputHandler::InputReplaceSelection( std::u16string_view aStr )
4705
0
{
4706
0
    if (!pRefViewSh)
4707
0
        pRefViewSh = pActiveViewSh;
4708
4709
0
    OSL_ENSURE(nFormSelEnd>=nFormSelStart,"Selection broken...");
4710
4711
0
    sal_Int32 nOldLen = nFormSelEnd - nFormSelStart;
4712
0
    sal_Int32 nNewLen = aStr.size();
4713
4714
0
    OUStringBuffer aBuf(aFormText);
4715
0
    if (nOldLen)
4716
0
        aBuf.remove(nFormSelStart, nOldLen);
4717
0
    if (nNewLen)
4718
0
        aBuf.insert(nFormSelStart, aStr);
4719
4720
0
    aFormText = aBuf.makeStringAndClear();
4721
4722
0
    nFormSelEnd = nFormSelStart + nNewLen;
4723
4724
0
    EditView* pView = GetFuncEditView();
4725
0
    if (pView)
4726
0
    {
4727
0
        pView->SetEditEngineUpdateLayout( false );
4728
0
        pView->getEditEngine().SetText( aFormText );
4729
0
        pView->SetSelection( ESelection(0,nFormSelStart, 0,nFormSelEnd) );
4730
0
        pView->SetEditEngineUpdateLayout( true );
4731
0
    }
4732
0
    bModified = true;
4733
0
}
4734
4735
void ScInputHandler::InputTurnOffWinEngine()
4736
0
{
4737
0
    bInOwnChange = true;                // disable ModifyHdl (reset below)
4738
4739
0
    eMode = SC_INPUT_NONE;
4740
    /* TODO: it would be better if there was some way to reset the input bar
4741
     * engine instead of deleting and having it recreate through
4742
     * GetFuncEditView(), but first least invasively let this fix fdo#71667 and
4743
     * fdo#72278 without reintroducing fdo#69971. */
4744
0
    StopInputWinEngine(true);
4745
4746
0
    bInOwnChange = false;
4747
0
}
4748
4749
/**
4750
 * ScInputHdlState
4751
 */
4752
ScInputHdlState::ScInputHdlState( const ScAddress& rCurPos,
4753
                                  const ScAddress& rStartPos,
4754
                                  const ScAddress& rEndPos,
4755
                                  OUString _aString,
4756
                                  const EditTextObject* pData )
4757
0
    :   aCursorPos  ( rCurPos ),
4758
0
        aStartPos   ( rStartPos ),
4759
0
        aEndPos     ( rEndPos ),
4760
0
        aString     (std::move( _aString )),
4761
0
        pEditData   ( pData ? pData->Clone() : nullptr )
4762
0
{
4763
0
}
4764
4765
ScInputHdlState::ScInputHdlState( const ScInputHdlState& rCpy )
4766
0
{
4767
0
    *this = rCpy;
4768
0
}
4769
4770
ScInputHdlState::~ScInputHdlState()
4771
0
{
4772
0
}
4773
4774
bool ScInputHdlState::operator==( const ScInputHdlState& r ) const
4775
0
{
4776
0
    return (    (aStartPos  == r.aStartPos)
4777
0
             && (aEndPos    == r.aEndPos)
4778
0
             && (aCursorPos == r.aCursorPos)
4779
0
             && (aString    == r.aString)
4780
0
             && ScGlobal::EETextObjEqual( pEditData.get(), r.pEditData.get() ) );
4781
0
}
4782
4783
ScInputHdlState& ScInputHdlState::operator=( const ScInputHdlState& r )
4784
0
{
4785
0
    if (this != &r)
4786
0
    {
4787
0
        aCursorPos  = r.aCursorPos;
4788
0
        aStartPos   = r.aStartPos;
4789
0
        aEndPos     = r.aEndPos;
4790
0
        aString     = r.aString;
4791
0
        pEditData.reset();
4792
0
        if (r.pEditData)
4793
0
            pEditData = r.pEditData->Clone();
4794
0
    }
4795
0
    return *this;
4796
0
}
4797
4798
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */