Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/navipi/content.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 <sal/config.h>
21
22
#include <string_view>
23
24
#include <svx/svditer.hxx>
25
#include <svx/svdobj.hxx>
26
#include <svx/svdview.hxx>
27
#include <sfx2/linkmgr.hxx>
28
#include <sfx2/docfile.hxx>
29
#include <sfx2/viewfrm.hxx>
30
#include <vcl/commandevent.hxx>
31
#include <vcl/svapp.hxx>
32
#include <vcl/weld/Menu.hxx>
33
#include <osl/diagnose.h>
34
#include <tools/urlobj.hxx>
35
#include <sal/log.hxx>
36
#include <unotools/charclass.hxx>
37
38
#include <content.hxx>
39
#include <navipi.hxx>
40
#include <global.hxx>
41
#include <docsh.hxx>
42
#include <docfunc.hxx>
43
#include <scmod.hxx>
44
#include <rangenam.hxx>
45
#include <dbdata.hxx>
46
#include <drwlayer.hxx>
47
#include <transobj.hxx>
48
#include <drwtrans.hxx>
49
#include <lnktrans.hxx>
50
#include <strings.hrc>
51
#include <scresid.hxx>
52
#include <bitmaps.hlst>
53
#include <arealink.hxx>
54
#include <navicfg.hxx>
55
#include <navsett.hxx>
56
#include <postit.hxx>
57
#include <tabvwsh.hxx>
58
#include <drawview.hxx>
59
#include <clipparam.hxx>
60
#include <markdata.hxx>
61
62
using namespace com::sun::star;
63
64
//  order of the categories in navigator -------------------------------------
65
66
const ScContentId pTypeList[int(ScContentId::LAST) + 1] =
67
{
68
    ScContentId::ROOT,            // ROOT (0) has to be at the front
69
    ScContentId::TABLE,
70
    ScContentId::RANGENAME,
71
    ScContentId::DBAREA,
72
    ScContentId::AREALINK,
73
    ScContentId::GRAPHIC,
74
    ScContentId::OLEOBJECT,
75
    ScContentId::NOTE,
76
    ScContentId::DRAWING
77
};
78
79
constexpr OUString aContentBmps[]=
80
{
81
    RID_BMP_CONTENT_TABLE,
82
    RID_BMP_CONTENT_RANGENAME,
83
    RID_BMP_CONTENT_DBAREA,
84
    RID_BMP_CONTENT_GRAPHIC,
85
    RID_BMP_CONTENT_OLEOBJECT,
86
    RID_BMP_CONTENT_NOTE,
87
    RID_BMP_CONTENT_AREALINK,
88
    RID_BMP_CONTENT_DRAWING
89
};
90
91
ScDocShell* ScContentTree::GetManualOrCurrent()
92
0
{
93
0
    ScDocShell* pSh = nullptr;
94
0
    if ( !aManualDoc.isEmpty() )
95
0
    {
96
0
        SfxObjectShell* pObjSh = SfxObjectShell::GetFirst( checkSfxObjectShell<ScDocShell> );
97
0
        while ( pObjSh && !pSh )
98
0
        {
99
0
            if ( pObjSh->GetTitle() == aManualDoc )
100
0
                pSh = dynamic_cast<ScDocShell*>( pObjSh  );
101
0
            pObjSh = SfxObjectShell::GetNext( *pObjSh, checkSfxObjectShell<ScDocShell> );
102
0
        }
103
0
    }
104
0
    else
105
0
    {
106
        //  only current when manual isn't set
107
        //  (so it's detected when the documents don't exists any longer)
108
109
0
        SfxViewShell* pViewSh = SfxViewShell::Current();
110
0
        if ( pViewSh )
111
0
        {
112
0
            SfxObjectShell* pObjSh = pViewSh->GetViewFrame().GetObjectShell();
113
0
            pSh = dynamic_cast<ScDocShell*>( pObjSh  );
114
0
        }
115
0
    }
116
117
0
    return pSh;
118
0
}
119
120
//          ScContentTree
121
122
ScContentTree::ScContentTree(std::unique_ptr<weld::TreeView> xTreeView, ScNavigatorDlg* pNavigatorDlg)
123
0
    : m_xTreeView(std::move(xTreeView))
124
0
    , m_xScratchIter(m_xTreeView->make_iterator())
125
0
    , m_xTransferObj(new ScLinkTransferObj)
126
0
    , pParentWindow(pNavigatorDlg)
127
0
    , nRootType(ScContentId::ROOT)
128
0
    , bIsInNavigatorDlg(false)
129
0
    , m_bFreeze(false)
130
0
    , m_nAsyncMouseReleaseId(nullptr)
131
0
{
132
0
    for (sal_uInt16 i = 0; i <= int(ScContentId::LAST); ++i)
133
0
        pPosList[pTypeList[i]] = i;         // inverse for searching
134
135
0
    m_aRootNodes[ScContentId::ROOT] = nullptr;
136
0
    for (sal_uInt16 i = 1; i < int(ScContentId::LAST); ++i)
137
0
        InitRoot(static_cast<ScContentId>(i));
138
139
0
    m_xTreeView->connect_item_activated(LINK(this, ScContentTree, ContentDoubleClickHdl));
140
0
    m_xTreeView->connect_mouse_release(LINK(this, ScContentTree, MouseReleaseHdl));
141
0
    m_xTreeView->connect_key_press(LINK(this, ScContentTree, KeyInputHdl));
142
0
    m_xTreeView->connect_command(LINK(this, ScContentTree, CommandHdl));
143
0
    m_xTreeView->connect_query_tooltip(LINK(this, ScContentTree, QueryTooltipHdl));
144
145
0
    rtl::Reference<TransferDataContainer> xHelper(m_xTransferObj);
146
0
    m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
147
148
0
    m_xTreeView->connect_drag_begin(LINK(this, ScContentTree, DragBeginHdl));
149
150
0
    m_xTreeView->set_selection_mode( SelectionMode::Single );
151
152
0
    m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 30,
153
0
                                  m_xTreeView->get_text_height() * 13);
154
0
}
155
156
ScContentTree::~ScContentTree()
157
0
{
158
0
    if (m_nAsyncMouseReleaseId)
159
0
    {
160
0
        Application::RemoveUserEvent(m_nAsyncMouseReleaseId);
161
0
        m_nAsyncMouseReleaseId = nullptr;
162
0
    }
163
0
}
164
165
const TranslateId SCSTR_CONTENT_ARY[] =
166
{
167
    SCSTR_CONTENT_ROOT,
168
    SCSTR_CONTENT_TABLE,
169
    SCSTR_CONTENT_RANGENAME,
170
    SCSTR_CONTENT_DBAREA,
171
    SCSTR_CONTENT_GRAPHIC,
172
    SCSTR_CONTENT_OLEOBJECT,
173
    SCSTR_CONTENT_NOTE,
174
    SCSTR_CONTENT_AREALINK,
175
    SCSTR_CONTENT_DRAWING
176
};
177
178
void ScContentTree::InitRoot( ScContentId nType )
179
0
{
180
0
    if ( nType == ScContentId::ROOT )
181
0
        return;
182
183
0
    if ( nRootType != ScContentId::ROOT && nRootType != nType )              // hidden ?
184
0
    {
185
0
        m_aRootNodes[nType] = nullptr;
186
0
        return;
187
0
    }
188
189
0
    auto const & aImage = aContentBmps[static_cast<int>(nType) - 1];
190
191
0
    OUString aName;
192
0
    if(comphelper::LibreOfficeKit::isActive())
193
0
    {
194
        //In case of LOK we may have many different ScContentTrees in different languages.
195
        //At creation time, we store what language we use, and then use it later too.
196
        //It does not work in the constructor, that is why it is here.
197
0
        if (!m_pResLocaleForLOK)
198
0
        {
199
0
            m_pResLocaleForLOK = std::make_unique<std::locale>(ScModule::get()->GetResLocale());
200
0
        }
201
0
        aName = Translate::get(SCSTR_CONTENT_ARY[static_cast<int>(nType)], *m_pResLocaleForLOK);
202
0
    }
203
0
    else
204
0
    {
205
0
        aName = ScResId(SCSTR_CONTENT_ARY[static_cast<int>(nType)]);
206
0
    }
207
    // back to the correct position:
208
0
    sal_uInt16 nPos = nRootType != ScContentId::ROOT ? 0 : pPosList[nType]-1;
209
0
    m_aRootNodes[nType] = m_xTreeView->make_iterator();
210
0
    m_xTreeView->insert(nullptr, nPos, &aName, nullptr, nullptr, nullptr, false, m_aRootNodes[nType].get());
211
0
    m_xTreeView->set_image(*m_aRootNodes[nType], aImage);
212
0
}
213
214
void ScContentTree::ClearAll()
215
0
{
216
    //There are one method in Control::SetUpdateMode(), and one override method SvTreeListBox::SetUpdateMode(). Here although
217
    //SvTreeListBox::SetUpdateMode() is called in refresh method, it only call SvTreeListBox::SetUpdateMode(), not Control::SetUpdateMode().
218
    //In m_xTreeView->clear(), Broadcast( LISTACTION_CLEARED ) will be called and finally, it will be trapped into the event yield() loop. And
219
    //the InitRoot() method won't be called. Then if a user click or press key to update the navigator tree, crash happens.
220
    //So the solution is to disable the UpdateMode of Control, then call Clear(), then recover the update mode
221
0
    bool bWasFrozen = m_bFreeze;
222
0
    if (!bWasFrozen)
223
0
        freeze();
224
0
    m_xTreeView->clear();
225
0
    if (!bWasFrozen)
226
0
        thaw();
227
0
    for (sal_uInt16 i=1; i<=int(ScContentId::LAST); i++)
228
0
        InitRoot(static_cast<ScContentId>(i));
229
0
}
230
231
void ScContentTree::ClearType(ScContentId nType)
232
0
{
233
0
    if (nType == ScContentId::ROOT)
234
0
        ClearAll();
235
0
    else
236
0
    {
237
0
        weld::TreeIter* pParent = m_aRootNodes[nType].get();
238
0
        if (!pParent || m_xTreeView->iter_has_child(*pParent)) // not if no children existing
239
0
        {
240
0
            if (pParent)
241
0
                m_xTreeView->remove(*pParent);          // with all children
242
0
            InitRoot( nType );                          // if needed insert anew
243
0
        }
244
0
    }
245
0
}
246
247
void ScContentTree::InsertContent( ScContentId nType, const OUString& rValue )
248
0
{
249
0
    weld::TreeIter* pParent = m_aRootNodes[nType].get();
250
0
    if (pParent)
251
0
    {
252
0
        m_xTreeView->insert(pParent, -1, &rValue, nullptr, nullptr, nullptr, false, m_xScratchIter.get());
253
0
        m_xTreeView->set_sensitive(*m_xScratchIter, true);
254
0
    }
255
0
    else
256
0
    {
257
0
        OSL_FAIL("InsertContent without parent");
258
0
    }
259
0
}
260
261
void ScContentTree::GetEntryIndexes(ScContentId& rnRootIndex, sal_uLong& rnChildIndex, const weld::TreeIter* pEntry) const
262
0
{
263
0
    rnRootIndex = ScContentId::ROOT;
264
0
    rnChildIndex = SC_CONTENT_NOCHILD;
265
266
0
    if( !pEntry ) {
267
0
        SAL_WARN("sc", "got a null TreeIter");
268
0
        return;
269
0
    }
270
271
0
    std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(pEntry));
272
0
    if (!m_xTreeView->iter_parent(*xParent))
273
0
        xParent.reset();
274
0
    bool bFound = false;
275
0
    for( int i = 1; !bFound && (i <= int(ScContentId::LAST)); ++i )
276
0
    {
277
0
        ScContentId nRoot = static_cast<ScContentId>(i);
278
0
        if (!m_aRootNodes[nRoot])
279
0
            continue;
280
0
        if (pEntry->equal(*m_aRootNodes[nRoot]))
281
0
        {
282
0
            rnRootIndex = nRoot;
283
0
            rnChildIndex = ~0UL;
284
0
            bFound = true;
285
0
        }
286
0
        else if (xParent && xParent->equal(*m_aRootNodes[nRoot]))
287
0
        {
288
0
            rnRootIndex = nRoot;
289
290
            // search the entry in all child entries of the parent
291
0
            sal_uLong nEntry = 0;
292
0
            std::unique_ptr<weld::TreeIter> xIterEntry(m_xTreeView->make_iterator(xParent.get()));
293
0
            bool bIterEntry = m_xTreeView->iter_children(*xIterEntry);
294
0
            while (!bFound && bIterEntry)
295
0
            {
296
0
                if (pEntry->equal(*xIterEntry))
297
0
                {
298
0
                    rnChildIndex = nEntry;
299
0
                    bFound = true;  // exit the while loop
300
0
                }
301
0
                bIterEntry = m_xTreeView->iter_next_sibling(*xIterEntry);
302
0
                ++nEntry;
303
0
            }
304
305
0
            bFound = true;  // exit the for loop
306
0
        }
307
0
    }
308
0
}
309
310
sal_uLong ScContentTree::GetChildIndex(const weld::TreeIter* pEntry) const
311
0
{
312
0
    ScContentId nRoot;
313
0
    sal_uLong nChild;
314
0
    GetEntryIndexes(nRoot, nChild, pEntry);
315
0
    return nChild;
316
0
}
317
318
static OUString lcl_GetDBAreaRange( const ScDocument* pDoc, const OUString& rDBName )
319
0
{
320
0
    OUString aRet;
321
0
    if (pDoc)
322
0
    {
323
0
        ScDBCollection* pDbNames = pDoc->GetDBCollection();
324
0
        const ScDBData* pData = pDbNames->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rDBName));
325
0
        if (pData)
326
0
        {
327
0
            ScRange aRange;
328
0
            pData->GetArea(aRange);
329
0
            aRet = aRange.Format(*pDoc, ScRefFlags::RANGE_ABS_3D);
330
0
        }
331
0
    }
332
0
    return aRet;
333
0
}
334
335
void ScContentTree::ActivateSelectedEntry()
336
0
{
337
0
    ScContentId nType;
338
0
    sal_uLong nChild;
339
0
    std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->get_cursor();
340
0
    GetEntryIndexes(nType, nChild, xEntry.get());
341
342
0
    if (xEntry && (nType != ScContentId::ROOT) && (nChild != SC_CONTENT_NOCHILD))
343
0
    {
344
0
        OUString aText(m_xTreeView->get_text(*xEntry));
345
346
0
        if ( !aManualDoc.isEmpty() )
347
0
            pParentWindow->SetCurrentDoc( aManualDoc );
348
349
0
        switch( nType )
350
0
        {
351
0
            case ScContentId::TABLE:
352
0
            {
353
                // tdf#133159 store current config before changing sheet
354
                // plausible that this should be done for all cases, but this
355
                // is the known case that needs it
356
0
                StoreNavigatorSettings();
357
0
                pParentWindow->SetCurrentTableStr( aText );
358
0
            }
359
0
            break;
360
361
0
            case ScContentId::RANGENAME:
362
0
                pParentWindow->SetCurrentCellStr( aText );
363
0
            break;
364
365
0
            case ScContentId::DBAREA:
366
0
            {
367
                //  If the same names of area and DB exists, then
368
                //  SID_CURRENTCELL takes the area name.
369
                //  Therefore for DB areas access them directly via address.
370
371
0
                OUString aRangeStr = lcl_GetDBAreaRange( GetSourceDocument(), aText );
372
0
                if (!aRangeStr.isEmpty())
373
0
                    pParentWindow->SetCurrentCellStr( aRangeStr );
374
0
            }
375
0
            break;
376
377
0
            case ScContentId::OLEOBJECT:
378
0
            case ScContentId::GRAPHIC:
379
0
            case ScContentId::DRAWING:
380
0
                pParentWindow->SetCurrentObject( aText );
381
0
            break;
382
383
0
            case ScContentId::NOTE:
384
0
            {
385
0
                ScAddress aPos = GetNotePos( nChild );
386
0
                pParentWindow->SetCurrentTable( aPos.Tab() );
387
0
                pParentWindow->SetCurrentCell( aPos.Col(), aPos.Row() );
388
                // Check whether the comment is currently visible and toggle its visibility
389
0
                ScDocument* pSrcDoc = GetSourceDocument();
390
0
                if (ScPostIt* pNote = pSrcDoc ? pSrcDoc->GetNote(aPos.Col(), aPos.Row(), aPos.Tab()) : nullptr)
391
0
                {
392
0
                    bool bVisible = pNote->IsCaptionShown();
393
                    // Effectivelly set the visibility of the comment
394
0
                    GetManualOrCurrent()->GetDocFunc().ShowNote(aPos, !bVisible);
395
                    // Put the note in edit mode
396
0
                    ScTabViewShell* pScTabViewShell = ScNavigatorDlg::GetTabViewShell();
397
0
                    pScTabViewShell->EditNote();
398
0
                }
399
0
            }
400
0
            break;
401
402
0
            case ScContentId::AREALINK:
403
0
            {
404
0
                const ScAreaLink* pLink = GetLink(nChild);
405
0
                ScDocument* pSrcDoc = GetSourceDocument();
406
0
                if (pLink && pSrcDoc)
407
0
                {
408
0
                    const ScRange& aRange = pLink->GetDestArea();
409
0
                    OUString aRangeStr(aRange.Format(*pSrcDoc, ScRefFlags::RANGE_ABS_3D, pSrcDoc->GetAddressConvention()));
410
0
                    pParentWindow->SetCurrentCellStr( aRangeStr );
411
0
                }
412
0
            }
413
0
            break;
414
0
            default: break;
415
0
        }
416
417
0
        ScNavigatorDlg::ReleaseFocus();     // set focus into document
418
0
    }
419
420
0
}
421
422
IMPL_LINK_NOARG(ScContentTree, ContentDoubleClickHdl, const weld::TreeIter&, bool)
423
0
{
424
0
    ActivateSelectedEntry();
425
0
    return false;
426
0
}
427
428
void ScContentTree::LaunchAsyncStoreNavigatorSettings()
429
0
{
430
0
    if (!m_nAsyncMouseReleaseId)
431
0
        m_nAsyncMouseReleaseId = Application::PostUserEvent(LINK(this, ScContentTree, AsyncStoreNavigatorSettings));
432
0
}
433
434
IMPL_LINK_NOARG(ScContentTree, MouseReleaseHdl, const MouseEvent&, bool)
435
0
{
436
0
    LaunchAsyncStoreNavigatorSettings();
437
0
    return false;
438
0
}
439
440
IMPL_LINK_NOARG(ScContentTree, AsyncStoreNavigatorSettings, void*, void)
441
0
{
442
0
    m_nAsyncMouseReleaseId = nullptr;
443
0
    StoreNavigatorSettings();
444
0
}
445
446
IMPL_LINK(ScContentTree, KeyInputHdl, const KeyEvent&, rKEvt, bool)
447
0
{
448
0
    bool bUsed = false;
449
450
0
    const vcl::KeyCode aCode = rKEvt.GetKeyCode();
451
0
    if (aCode.GetCode() == KEY_RETURN)
452
0
    {
453
0
        switch (aCode.GetModifier())
454
0
        {
455
0
            case KEY_MOD1:
456
0
                ToggleRoot();       // toggle root mode (as in Writer)
457
0
                bUsed = true;
458
0
                break;
459
0
            case 0:
460
0
            {
461
0
                if (std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->get_cursor())
462
0
                {
463
0
                    ScContentId nType;
464
0
                    sal_uLong nChild;
465
0
                    GetEntryIndexes(nType, nChild, xEntry.get());
466
467
0
                    if (nType != ScContentId::ROOT && nChild == SC_CONTENT_NOCHILD)
468
0
                    {
469
0
                        if (m_xTreeView->get_row_expanded(*xEntry))
470
0
                            m_xTreeView->collapse_row(*xEntry);
471
0
                        else
472
0
                            m_xTreeView->expand_row(*xEntry);
473
0
                    }
474
0
                    else
475
0
                    {
476
                        // activate entry as if double clicked
477
0
                        ActivateSelectedEntry();
478
0
                    }
479
0
                }
480
481
0
                bUsed = true;
482
0
            }
483
0
            break;
484
0
        }
485
0
    }
486
    //Make KEY_SPACE has same function as DoubleClick, and realize
487
    //multi-selection.
488
0
    if ( bIsInNavigatorDlg )
489
0
    {
490
0
        if(aCode.GetCode() == KEY_SPACE )
491
0
        {
492
0
            bUsed = true;
493
0
            ScContentId nType;
494
0
            sal_uLong nChild;
495
0
            std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->get_cursor();
496
0
            GetEntryIndexes(nType, nChild, xEntry.get());
497
498
0
            if (xEntry && (nType != ScContentId::ROOT) && (nChild != SC_CONTENT_NOCHILD))
499
0
            {
500
0
                OUString aText(m_xTreeView->get_text(*xEntry));
501
0
                if (!aManualDoc.isEmpty())
502
0
                    pParentWindow->SetCurrentDoc( aManualDoc );
503
0
                switch (nType)
504
0
                {
505
0
                    case ScContentId::OLEOBJECT:
506
0
                    case ScContentId::GRAPHIC:
507
0
                    case ScContentId::DRAWING:
508
0
                    {
509
0
                        ScDrawView* pScDrawView = nullptr;
510
0
                        ScTabViewShell* pScTabViewShell = ScNavigatorDlg::GetTabViewShell();
511
0
                        if (pScTabViewShell)
512
0
                            pScDrawView = pScTabViewShell->GetViewData().GetScDrawView();
513
0
                        if (pScDrawView)
514
0
                        {
515
0
                            pScDrawView->SelectCurrentViewObject(aText);
516
0
                            bool bHasMakredObject = false;
517
0
                            weld::TreeIter* pParent = m_aRootNodes[nType].get();
518
0
                            std::unique_ptr<weld::TreeIter> xBeginEntry(m_xTreeView->make_iterator(pParent));
519
0
                            bool bBeginEntry = false;
520
0
                            if (pParent)
521
0
                                bBeginEntry = m_xTreeView->iter_children(*xBeginEntry);
522
0
                            while (bBeginEntry)
523
0
                            {
524
0
                                OUString aTempText(m_xTreeView->get_text(*xBeginEntry));
525
0
                                if( pScDrawView->GetObjectIsMarked( pScDrawView->GetObjectByName( aTempText ) ) )
526
0
                                {
527
0
                                    bHasMakredObject = true;
528
0
                                    break;
529
0
                                }
530
0
                                bBeginEntry = m_xTreeView->iter_next(*xBeginEntry);
531
0
                            }
532
0
                            if (!bHasMakredObject && pScTabViewShell)
533
0
                                pScTabViewShell->SetDrawShell(false);
534
0
                        }
535
0
                        break;
536
0
                    }
537
0
                    default:
538
0
                        break;
539
0
                }
540
0
            }
541
0
        }
542
0
    }
543
544
0
    if (!bUsed)
545
0
    {
546
0
        if (aCode.GetCode() == KEY_F5)
547
0
            StoreNavigatorSettings();
548
0
        else
549
0
            LaunchAsyncStoreNavigatorSettings();
550
0
    }
551
552
0
    return bUsed;
553
0
}
554
555
IMPL_LINK(ScContentTree, CommandHdl, const CommandEvent&, rCEvt, bool)
556
0
{
557
0
    bool bDone = false;
558
559
0
    ScContentId nType;
560
0
    sal_uLong nChild;
561
0
    std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->get_cursor();
562
0
    GetEntryIndexes(nType, nChild, xEntry.get());
563
564
0
    switch ( rCEvt.GetCommand() )
565
0
    {
566
0
        case CommandEventId::ContextMenu:
567
0
            {
568
0
                if (comphelper::LibreOfficeKit::isActive())
569
0
                    break;
570
571
                //  drag-and-drop mode
572
0
                std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), u"modules/scalc/ui/dropmenu.ui"_ustr));
573
0
                std::unique_ptr<weld::Menu> xPop(xBuilder->weld_menu(u"contextmenu"_ustr));
574
0
                std::unique_ptr<weld::Menu> xDropMenu(xBuilder->weld_menu(u"dragmodesubmenu"_ustr));
575
576
0
                switch (pParentWindow->GetDropMode())
577
0
                {
578
0
                    case 0:
579
0
                        xDropMenu->set_active(u"hyperlink"_ustr, true);
580
0
                        break;
581
0
                    case 1:
582
0
                        xDropMenu->set_active(u"link"_ustr, true);
583
0
                        break;
584
0
                    case 2:
585
0
                        xDropMenu->set_active(u"copy"_ustr, true);
586
0
                        break;
587
0
                }
588
589
                //  displayed document
590
0
                std::unique_ptr<weld::Menu> xDocMenu(xBuilder->weld_menu(u"displaymenu"_ustr));
591
0
                sal_uInt16 i=0;
592
0
                OUString sActive;
593
0
                OUString sId;
594
                //  loaded documents
595
0
                ScDocShell* pCurrentSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current()  );
596
0
                SfxObjectShell* pSh = SfxObjectShell::GetFirst();
597
0
                while ( pSh )
598
0
                {
599
0
                    if ( dynamic_cast<const ScDocShell*>( pSh) !=  nullptr )
600
0
                    {
601
0
                        OUString aName = pSh->GetTitle();
602
0
                        OUString aEntry = aName;
603
0
                        if ( pSh == pCurrentSh )
604
0
                            aEntry += pParentWindow->aStrActive;
605
0
                        else
606
0
                            aEntry += pParentWindow->aStrNotActive;
607
0
                        ++i;
608
0
                        sId = "document" + OUString::number(i);
609
0
                        xDocMenu->append_radio(sId, aEntry);
610
0
                        if (aName == aManualDoc)
611
0
                            sActive = sId;
612
0
                    }
613
0
                    pSh = SfxObjectShell::GetNext( *pSh );
614
0
                }
615
                //  "active window"
616
0
                ++i;
617
0
                sId = "document" + OUString::number(i);
618
0
                xDocMenu->append_radio(sId, pParentWindow->aStrActiveWin);
619
0
                if (aManualDoc.isEmpty())
620
0
                    sActive = sId;
621
0
                xDocMenu->set_active(sActive, true);
622
623
                // Edit/Delete Comments are only visible for comments
624
0
                if (nType != ScContentId::NOTE)
625
0
                {
626
0
                    xPop->set_visible(u"edit"_ustr, false);
627
0
                    xPop->set_visible(u"delete"_ustr, false);
628
0
                }
629
630
0
                OUString sIdent = xPop->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1)));
631
0
                if (sIdent == "hyperlink")
632
0
                    pParentWindow->SetDropMode(0);
633
0
                else if (sIdent == "link")
634
0
                    pParentWindow->SetDropMode(1);
635
0
                else if (sIdent == "copy")
636
0
                    pParentWindow->SetDropMode(2);
637
0
                else if (sIdent.startsWith("document"))
638
0
                {
639
0
                    OUString aName = xDocMenu->get_label(sIdent);
640
0
                    SelectDoc(aName);
641
0
                }
642
0
                else if (sIdent == "edit")
643
0
                {
644
0
                    ScAddress aPos = GetNotePos( nChild );
645
0
                    pParentWindow->SetCurrentTable( aPos.Tab() );
646
0
                    pParentWindow->SetCurrentCell( aPos.Col(), aPos.Row() );
647
0
                    ScDocument* pSrcDoc = GetSourceDocument();
648
0
                    if (pSrcDoc->GetNote(aPos.Col(), aPos.Row(), aPos.Tab()))
649
0
                    {
650
                        // Make the note visible and put it in edit mode
651
0
                        GetManualOrCurrent()->GetDocFunc().ShowNote(aPos, true);
652
0
                        ScTabViewShell* pScTabViewShell = ScNavigatorDlg::GetTabViewShell();
653
0
                        pScTabViewShell->EditNote();
654
0
                        bDone = true;
655
0
                    }
656
0
                }
657
0
                else if (sIdent == "delete")
658
0
                {
659
0
                    ScAddress aPos = GetNotePos(nChild);
660
0
                    pParentWindow->SetCurrentTable(aPos.Tab());
661
0
                    pParentWindow->SetCurrentCell(aPos.Col(), aPos.Row());
662
0
                    ScTabViewShell* pScTabViewShell = ScNavigatorDlg::GetTabViewShell();
663
0
                    pScTabViewShell->DeleteContents(InsertDeleteFlags::NOTE);
664
0
                }
665
0
            }
666
0
            break;
667
0
            default: break;
668
0
    }
669
670
0
    return bDone;
671
0
}
672
673
IMPL_LINK(ScContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString)
674
0
{
675
0
    OUString aHelpText;
676
677
0
    std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
678
0
    if (!m_xTreeView->iter_parent(*xParent))
679
0
        xParent.reset();
680
681
0
    if (!xParent)                                 // Top-Level ?
682
0
    {
683
0
        aHelpText = OUString::number(m_xTreeView->iter_n_children(rEntry)) +
684
0
                    " " + m_xTreeView->get_text(rEntry);
685
0
    }
686
0
    else if (m_aRootNodes[ScContentId::NOTE] && xParent->equal(*m_aRootNodes[ScContentId::NOTE]))
687
0
    {
688
0
        aHelpText = m_xTreeView->get_text(rEntry);     // notes as help text
689
0
    }
690
0
    else if (m_aRootNodes[ScContentId::AREALINK] && xParent->equal(*m_aRootNodes[ScContentId::AREALINK]))
691
0
    {
692
0
        auto nIndex = GetChildIndex(&rEntry);
693
0
        if (nIndex != SC_CONTENT_NOCHILD)
694
0
        {
695
0
            const ScAreaLink* pLink = GetLink(nIndex);
696
0
            if (pLink)
697
0
            {
698
0
                aHelpText = pLink->GetFile();           // source file as help text
699
0
            }
700
0
        }
701
0
    }
702
703
0
    return aHelpText;
704
0
}
705
706
ScDocument* ScContentTree::GetSourceDocument()
707
0
{
708
0
    ScDocShell* pSh = GetManualOrCurrent();
709
0
    if (pSh)
710
0
        return &pSh->GetDocument();
711
0
    return nullptr;
712
0
}
713
714
void ScContentTree::Refresh( ScContentId nType )
715
0
{
716
    //  if nothing has changed the cancel right away (against flicker)
717
0
    if ( nType == ScContentId::NOTE )
718
0
        if (!NoteStringsChanged())
719
0
            return;
720
0
    if ( nType == ScContentId::GRAPHIC )
721
0
        if (!DrawNamesChanged(ScContentId::GRAPHIC))
722
0
            return;
723
0
    if ( nType == ScContentId::OLEOBJECT )
724
0
        if (!DrawNamesChanged(ScContentId::OLEOBJECT))
725
0
            return;
726
0
    if ( nType == ScContentId::DRAWING )
727
0
        if (!DrawNamesChanged(ScContentId::DRAWING))
728
0
            return;
729
730
0
    freeze();
731
732
0
    ClearType( nType );
733
734
0
    if ( nType == ScContentId::ROOT || nType == ScContentId::TABLE )
735
0
        GetTableNames();
736
0
    if ( nType == ScContentId::ROOT || nType == ScContentId::RANGENAME )
737
0
        GetAreaNames();
738
0
    if ( nType == ScContentId::ROOT || nType == ScContentId::DBAREA )
739
0
        GetDbNames();
740
0
    if ( nType == ScContentId::ROOT || nType == ScContentId::GRAPHIC )
741
0
        GetGraphicNames();
742
0
    if ( nType == ScContentId::ROOT || nType == ScContentId::OLEOBJECT )
743
0
        GetOleNames();
744
0
    if ( nType == ScContentId::ROOT || nType == ScContentId::DRAWING )
745
0
        GetDrawingNames();
746
0
    if ( nType == ScContentId::ROOT || nType == ScContentId::NOTE )
747
0
        GetNoteStrings();
748
0
    if ( nType == ScContentId::ROOT || nType == ScContentId::AREALINK )
749
0
        GetLinkNames();
750
751
0
    thaw();
752
753
0
    ApplyNavigatorSettings();
754
0
}
755
756
void ScContentTree::GetTableNames()
757
0
{
758
0
    if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::TABLE )       // hidden ?
759
0
        return;
760
761
0
    ScDocument* pDoc = GetSourceDocument();
762
0
    if (!pDoc)
763
0
        return;
764
765
0
    OUString aName;
766
0
    SCTAB nCount = pDoc->GetTableCount();
767
0
    for ( SCTAB i=0; i<nCount; i++ )
768
0
    {
769
0
        pDoc->GetName( i, aName );
770
0
        InsertContent( ScContentId::TABLE, aName );
771
0
    }
772
0
}
773
774
namespace {
775
776
OUString createLocalRangeName(std::u16string_view rName, std::u16string_view rTableName)
777
0
{
778
0
    return OUString::Concat(rName) + " (" + rTableName + ")";
779
0
}
780
}
781
782
void ScContentTree::GetAreaNames()
783
0
{
784
0
    if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::RANGENAME )       // hidden ?
785
0
        return;
786
787
0
    ScDocument* pDoc = GetSourceDocument();
788
0
    if (!pDoc)
789
0
        return;
790
791
0
    ScRange aDummy;
792
0
    std::set<OUString> aSet;
793
0
    ScRangeName* pRangeNames = pDoc->GetRangeName();
794
0
    for (const auto& rEntry : *pRangeNames)
795
0
    {
796
0
        if (rEntry.second->IsValidReference(aDummy))
797
0
            aSet.insert(rEntry.second->GetName());
798
0
    }
799
0
    for (SCTAB i = 0; i < pDoc->GetTableCount(); ++i)
800
0
    {
801
0
        ScRangeName* pLocalRangeName = pDoc->GetRangeName(i);
802
0
        if (pLocalRangeName && !pLocalRangeName->empty())
803
0
        {
804
0
            OUString aTableName;
805
0
            pDoc->GetName(i, aTableName);
806
0
            for (const auto& rEntry : *pLocalRangeName)
807
0
            {
808
0
                if (rEntry.second->IsValidReference(aDummy))
809
0
                    aSet.insert(createLocalRangeName(rEntry.second->GetName(), aTableName));
810
0
            }
811
0
        }
812
0
    }
813
814
0
    for (const auto& rItem : aSet)
815
0
    {
816
0
        InsertContent(ScContentId::RANGENAME, rItem);
817
0
    }
818
0
}
819
820
void ScContentTree::GetDbNames()
821
0
{
822
0
    if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::DBAREA )      // hidden ?
823
0
        return;
824
825
0
    ScDocument* pDoc = GetSourceDocument();
826
0
    if (!pDoc)
827
0
        return;
828
829
0
    ScDBCollection* pDbNames = pDoc->GetDBCollection();
830
0
    const ScDBCollection::NamedDBs& rDBs = pDbNames->getNamedDBs();
831
0
    for (const auto& rxDB : rDBs)
832
0
    {
833
0
        const OUString& aStrName = rxDB->GetName();
834
0
        InsertContent(ScContentId::DBAREA, aStrName);
835
0
    }
836
0
}
837
838
bool ScContentTree::IsPartOfType( ScContentId nContentType, SdrObjKind nObjIdentifier )
839
0
{
840
0
    bool bRet = false;
841
0
    switch ( nContentType )
842
0
    {
843
0
        case ScContentId::GRAPHIC:
844
0
            bRet = ( nObjIdentifier == SdrObjKind::Graphic );
845
0
            break;
846
0
        case ScContentId::OLEOBJECT:
847
0
            bRet = ( nObjIdentifier == SdrObjKind::OLE2 );
848
0
            break;
849
0
        case ScContentId::DRAWING:
850
0
            bRet = ( nObjIdentifier != SdrObjKind::Graphic && nObjIdentifier != SdrObjKind::OLE2 );    // everything else
851
0
            break;
852
0
        default:
853
0
            OSL_FAIL("unknown content type");
854
0
    }
855
0
    return bRet;
856
0
}
857
858
constexpr int MAX_TREE_NODES = 1000;
859
860
void ScContentTree::GetDrawNames( ScContentId nType )
861
0
{
862
0
    if (!bIsInNavigatorDlg)
863
0
        return;
864
865
0
    if ( nRootType != ScContentId::ROOT && nRootType != nType )              // hidden ?
866
0
        return;
867
868
0
    ScDocument* pDoc = GetSourceDocument();
869
0
    if (!pDoc)
870
0
        return;
871
872
0
    ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
873
0
    if (!pDrawLayer)
874
0
        return;
875
876
0
    ScDocShell* pShell = pDoc->GetDocumentShell();
877
0
    if (!pShell)
878
0
        return;
879
880
    // iterate in flat mode for groups
881
0
    SdrIterMode eIter = ( nType == ScContentId::DRAWING ) ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups;
882
883
0
    std::vector<OUString> aNames;
884
0
    SCTAB nTabCount = pDoc->GetTableCount();
885
0
    for (SCTAB nTab=0; nTab<nTabCount; nTab++)
886
0
    {
887
0
        SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
888
0
        OSL_ENSURE(pPage,"Page ?");
889
0
        if (!pPage)
890
0
            continue;
891
0
        SdrObjListIter aIter(pPage, eIter);
892
0
        SdrObject* pObject = aIter.Next();
893
0
        while (pObject)
894
0
        {
895
0
            if (IsPartOfType(nType, pObject->GetObjIdentifier()))
896
0
            {
897
0
                OUString aName = ScDrawLayer::GetVisibleName(pObject);
898
0
                if (!aName.isEmpty())
899
0
                    aNames.push_back(aName);
900
0
                if (aNames.size() > MAX_TREE_NODES)
901
0
                {
902
0
                    SAL_WARN("sc", "too many tree nodes, ignoring the rest");
903
0
                    break;
904
0
                }
905
0
            }
906
0
            pObject = aIter.Next();
907
0
        }
908
0
    }
909
910
0
    weld::TreeIter* pParent = m_aRootNodes[nType].get();
911
0
    assert(pParent && "InsertContent without parent");
912
    // insert all of these in one go under pParent
913
0
    m_xTreeView->bulk_insert_for_each(aNames.size(), [this, &aNames](weld::TreeIter& rIter, int nIndex) {
914
0
        m_xTreeView->set_text(rIter, aNames[nIndex], 0);
915
0
        m_xTreeView->set_sensitive(rIter, true);
916
0
    }, pParent);
917
0
}
918
919
void ScContentTree::GetGraphicNames()
920
0
{
921
0
    GetDrawNames( ScContentId::GRAPHIC );
922
0
}
923
924
void ScContentTree::GetOleNames()
925
0
{
926
0
    GetDrawNames( ScContentId::OLEOBJECT );
927
0
}
928
929
void ScContentTree::GetDrawingNames()
930
0
{
931
0
    GetDrawNames( ScContentId::DRAWING );
932
0
}
933
934
void ScContentTree::GetLinkNames()
935
0
{
936
0
    if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::AREALINK )                // hidden ?
937
0
        return;
938
939
0
    ScDocument* pDoc = GetSourceDocument();
940
0
    if (!pDoc)
941
0
        return;
942
943
0
    sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
944
0
    assert(pLinkManager && "no LinkManager on document?");
945
0
    const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
946
0
    sal_uInt16 nCount = rLinks.size();
947
0
    for (sal_uInt16 i=0; i<nCount; i++)
948
0
    {
949
0
        ::sfx2::SvBaseLink* pBase = rLinks[i].get();
950
0
        if (auto pScAreaLink = dynamic_cast<const ScAreaLink*>( pBase))
951
0
            InsertContent( ScContentId::AREALINK, pScAreaLink->GetSource() );
952
953
            //  insert in list the names of source areas
954
0
    }
955
0
}
956
957
const ScAreaLink* ScContentTree::GetLink( sal_uLong nIndex )
958
0
{
959
0
    ScDocument* pDoc = GetSourceDocument();
960
0
    if (!pDoc)
961
0
        return nullptr;
962
963
0
    sal_uLong nFound = 0;
964
0
    sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
965
0
    assert(pLinkManager && "no LinkManager on document?");
966
0
    const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
967
0
    sal_uInt16 nCount = rLinks.size();
968
0
    for (sal_uInt16 i=0; i<nCount; i++)
969
0
    {
970
0
        ::sfx2::SvBaseLink* pBase = rLinks[i].get();
971
0
        if (auto pAreaLink = dynamic_cast<const ScAreaLink*>( pBase))
972
0
        {
973
0
            if (nFound == nIndex)
974
0
                return pAreaLink;
975
0
            ++nFound;
976
0
        }
977
0
    }
978
979
0
    OSL_FAIL("link not found");
980
0
    return nullptr;
981
0
}
982
983
static OUString lcl_NoteString( const ScPostIt& rNote )
984
0
{
985
0
    return rNote.GetText().replace('\n', ' ');
986
0
}
987
988
void ScContentTree::GetNoteStrings()
989
0
{
990
0
    if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::NOTE )        // hidden ?
991
0
        return;
992
993
0
    ScDocument* pDoc = GetSourceDocument();
994
0
    if (!pDoc)
995
0
        return;
996
997
    // loop over cell notes
998
0
    std::vector<sc::NoteEntry> aEntries;
999
0
    pDoc->GetAllNoteEntries(aEntries);
1000
0
    weld::TreeIter* pParent = m_aRootNodes[ScContentId::NOTE].get();
1001
0
    for (const auto& rEntry : aEntries)
1002
0
    {
1003
0
        OUString aValue = lcl_NoteString(*rEntry.mpNote);
1004
0
        OUString aId = OUString::number(rEntry.mpNote->GetId());
1005
0
        m_xTreeView->insert(pParent, -1, &aValue, &aId, nullptr, nullptr, false, m_xScratchIter.get());
1006
0
        m_xTreeView->set_sensitive(*m_xScratchIter, true);
1007
0
    }
1008
0
}
1009
1010
ScAddress ScContentTree::GetNotePos( sal_uLong nIndex )
1011
0
{
1012
0
    ScDocument* pDoc = GetSourceDocument();
1013
0
    if (!pDoc)
1014
0
        return ScAddress();
1015
1016
0
    return pDoc->GetNotePosition(nIndex);
1017
0
}
1018
1019
bool ScContentTree::NoteStringsChanged()
1020
0
{
1021
0
    ScDocument* pDoc = GetSourceDocument();
1022
0
    if (!pDoc)
1023
0
        return false;
1024
1025
0
    weld::TreeIter* pParent = m_aRootNodes[ScContentId::NOTE].get();
1026
0
    if (!pParent)
1027
0
        return false;
1028
1029
0
    std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pParent));
1030
0
    bool bEntry = m_xTreeView->iter_children(*xEntry);
1031
1032
0
    std::vector<sc::NoteEntry> aEntries;
1033
0
    pDoc->GetAllNoteEntries(aEntries);
1034
0
    for (const auto& rEntry : aEntries)
1035
0
    {
1036
0
        const ScPostIt* pNote = rEntry.mpNote;
1037
0
        if (!bEntry)
1038
0
            return true;
1039
1040
0
        if (lcl_NoteString(*pNote) != m_xTreeView->get_text(*xEntry))
1041
0
            return true;
1042
1043
0
        bEntry = m_xTreeView->iter_next_sibling(*xEntry);
1044
0
    }
1045
1046
0
    return bEntry;
1047
0
}
1048
1049
bool ScContentTree::DrawNamesChanged( ScContentId nType )
1050
0
{
1051
0
    ScDocument* pDoc = GetSourceDocument();
1052
0
    if (!pDoc)
1053
0
        return false;
1054
1055
0
    weld::TreeIter* pParent = m_aRootNodes[nType].get();
1056
0
    if (!pParent)
1057
0
        return false;
1058
1059
0
    std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pParent));
1060
0
    bool bEntry = m_xTreeView->iter_children(*xEntry);
1061
1062
    // iterate in flat mode for groups
1063
0
    SdrIterMode eIter = ( nType == ScContentId::DRAWING ) ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups;
1064
1065
0
    bool bEqual = true;
1066
0
    ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
1067
0
    ScDocShell* pShell = pDoc->GetDocumentShell();
1068
0
    if (pDrawLayer && pShell)
1069
0
    {
1070
0
        SCTAB nTabCount = pDoc->GetTableCount();
1071
0
        for (SCTAB nTab=0; nTab<nTabCount && bEqual; nTab++)
1072
0
        {
1073
0
            SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
1074
0
            OSL_ENSURE(pPage,"Page ?");
1075
0
            if (pPage)
1076
0
            {
1077
0
                SdrObjListIter aIter( pPage, eIter );
1078
0
                SdrObject* pObject = aIter.Next();
1079
0
                while (pObject && bEqual)
1080
0
                {
1081
0
                    if ( IsPartOfType( nType, pObject->GetObjIdentifier() ) )
1082
0
                    {
1083
0
                        if ( !bEntry )
1084
0
                            bEqual = false;
1085
0
                        else
1086
0
                        {
1087
0
                            if (ScDrawLayer::GetVisibleName(pObject) != m_xTreeView->get_text(*xEntry))
1088
0
                                bEqual = false;
1089
1090
0
                            bEntry = m_xTreeView->iter_next_sibling(*xEntry);
1091
0
                        }
1092
0
                    }
1093
0
                    pObject = aIter.Next();
1094
0
                }
1095
0
            }
1096
0
        }
1097
0
    }
1098
1099
0
    if ( bEntry )
1100
0
        bEqual = false;             // anything else
1101
1102
0
    return !bEqual;
1103
0
}
1104
1105
static bool lcl_GetRange( const ScDocument& rDoc, ScContentId nType, const OUString& rName, ScRange& rRange )
1106
0
{
1107
0
    bool bFound = false;
1108
1109
0
    if ( nType == ScContentId::RANGENAME )
1110
0
    {
1111
0
        ScRangeName* pList = rDoc.GetRangeName();
1112
0
        if (pList)
1113
0
        {
1114
0
            const ScRangeData* p = pList->findByUpperName(ScGlobal::getCharClass().uppercase(rName));
1115
0
            if (p && p->IsValidReference(rRange))
1116
0
                bFound = true;
1117
0
        }
1118
0
    }
1119
0
    else if ( nType == ScContentId::DBAREA )
1120
0
    {
1121
0
        ScDBCollection* pList = rDoc.GetDBCollection();
1122
0
        if (pList)
1123
0
        {
1124
0
            const ScDBData* p = pList->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rName));
1125
0
            if (p)
1126
0
            {
1127
0
                SCTAB nTab;
1128
0
                SCCOL nCol1, nCol2;
1129
0
                SCROW nRow1, nRow2;
1130
0
                p->GetArea(nTab, nCol1, nRow1, nCol2, nRow2);
1131
0
                rRange = ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab);
1132
0
                bFound = true;
1133
0
            }
1134
0
        }
1135
0
    }
1136
1137
0
    return bFound;
1138
0
}
1139
1140
static bool lcl_DoDragObject( ScDocShell& rSrcShell, std::u16string_view rName, ScContentId nType, weld::TreeView& rTreeView )
1141
0
{
1142
0
    bool bDisallow = true;
1143
1144
0
    ScDocument& rSrcDoc = rSrcShell.GetDocument();
1145
0
    ScDrawLayer* pModel = rSrcDoc.GetDrawLayer();
1146
0
    if (pModel)
1147
0
    {
1148
0
        bool bOle = ( nType == ScContentId::OLEOBJECT );
1149
0
        bool bGraf = ( nType == ScContentId::GRAPHIC );
1150
0
        SdrObjKind nDrawId = bOle ? SdrObjKind::OLE2 : ( bGraf ? SdrObjKind::Graphic : SdrObjKind::Group );
1151
0
        SCTAB nTab = 0;
1152
0
        SdrObject* pObject = pModel->GetNamedObject( rName, nDrawId, nTab );
1153
0
        if (pObject)
1154
0
        {
1155
0
            SdrView aEditView(*pModel);
1156
0
            aEditView.ShowSdrPage(aEditView.GetModel().GetPage(nTab));
1157
0
            SdrPageView* pPV = aEditView.GetSdrPageView();
1158
0
            aEditView.MarkObj(pObject, pPV);
1159
1160
            // tdf125520 this is a D&D-start potentially with an OLE object. If
1161
            // so, we need to do similar as e.g. in ScDrawView::BeginDrag so that
1162
            // the temporary SdrModel for transfer does have a GetPersist() so
1163
            // that the EmbeddedObjectContainer gets copied. We need no CheckOle
1164
            // here, test is simpler.
1165
0
            ScDocShellRef aDragShellRef;
1166
0
            if(SdrObjKind::OLE2 == pObject->GetObjIdentifier())
1167
0
            {
1168
0
                aDragShellRef = new ScDocShell;     // DocShell needs a Ref immediately
1169
0
                aDragShellRef->DoInitNew();
1170
0
            }
1171
1172
0
            ScDrawLayer::SetGlobalDrawPersist(aDragShellRef.get());
1173
0
            std::unique_ptr<SdrModel> pDragModel(aEditView.CreateMarkedObjModel());
1174
0
            ScDrawLayer::SetGlobalDrawPersist(nullptr);
1175
1176
0
            TransferableObjectDescriptor aObjDesc;
1177
0
            rSrcShell.FillTransferableObjectDescriptor( aObjDesc );
1178
0
            aObjDesc.maDisplayName = rSrcShell.GetMedium()->GetURLObject().GetURLNoPass();
1179
            // maSize is set in ScDrawTransferObj ctor
1180
1181
0
            rtl::Reference<ScDrawTransferObj> pTransferObj = new ScDrawTransferObj( std::move(pDragModel), rSrcShell, std::move(aObjDesc) );
1182
1183
0
            pTransferObj->SetDragSourceObj( *pObject, nTab );
1184
0
            pTransferObj->SetDragSourceFlags(ScDragSrc::Navigator);
1185
1186
0
            ScModule::get()->SetDragObject(nullptr, pTransferObj.get());
1187
1188
0
            rtl::Reference<TransferDataContainer> xHelper(pTransferObj);
1189
0
            rTreeView.enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
1190
1191
0
            bDisallow = false;
1192
0
        }
1193
0
    }
1194
1195
0
    return bDisallow;
1196
0
}
1197
1198
static bool lcl_DoDragCells( ScDocShell& rSrcShell, const ScRange& rRange, ScDragSrc nFlags, weld::TreeView& rTreeView )
1199
0
{
1200
0
    bool bDisallow = true;
1201
1202
0
    ScDocument& rSrcDoc = rSrcShell.GetDocument();
1203
0
    ScMarkData aMark(rSrcDoc.GetSheetLimits());
1204
0
    aMark.SelectTable( rRange.aStart.Tab(), true );
1205
0
    aMark.SetMarkArea( rRange );
1206
1207
0
    if ( !rSrcDoc.HasSelectedBlockMatrixFragment( rRange.aStart.Col(), rRange.aStart.Row(),
1208
0
                                                   rRange.aEnd.Col(),   rRange.aEnd.Row(),
1209
0
                                                   aMark ) )
1210
0
    {
1211
0
        ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
1212
0
        ScClipParam aClipParam(rRange, false);
1213
0
        rSrcDoc.CopyToClip(aClipParam, pClipDoc.get(), &aMark, false, false);
1214
        // pClipDoc->ExtendMerge( rRange, sal_True );
1215
1216
0
        TransferableObjectDescriptor aObjDesc;
1217
0
        rSrcShell.FillTransferableObjectDescriptor( aObjDesc );
1218
0
        aObjDesc.maDisplayName = rSrcShell.GetMedium()->GetURLObject().GetURLNoPass();
1219
        // maSize is set in ScTransferObj ctor
1220
1221
0
        rtl::Reference<ScTransferObj> pTransferObj = new ScTransferObj( std::move(pClipDoc), std::move(aObjDesc) );
1222
1223
0
        pTransferObj->SetDragSource( &rSrcShell, aMark );
1224
0
        pTransferObj->SetDragSourceFlags( nFlags );
1225
1226
0
        ScModule::get()->SetDragObject(pTransferObj.get(), nullptr); // for internal D&D
1227
1228
0
        rtl::Reference<TransferDataContainer> xHelper(pTransferObj);
1229
0
        rTreeView.enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
1230
1231
0
        bDisallow = false;
1232
0
    }
1233
1234
0
    return bDisallow;
1235
0
}
1236
1237
IMPL_LINK(ScContentTree, DragBeginHdl, bool&, rUnsetDragIcon, bool)
1238
0
{
1239
0
    rUnsetDragIcon = true;
1240
1241
0
    StoreNavigatorSettings();
1242
1243
0
    bool bDisallow = true;
1244
1245
0
    ScModule* pScMod = ScModule::get();
1246
1247
0
    ScContentId nType;
1248
0
    sal_uLong nChild;
1249
1250
0
    std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->get_cursor();
1251
0
    GetEntryIndexes(nType, nChild, xEntry.get());
1252
1253
0
    if( xEntry &&
1254
0
        (nChild != SC_CONTENT_NOCHILD) &&
1255
0
        (nType != ScContentId::ROOT) &&
1256
0
        (nType != ScContentId::NOTE) &&
1257
0
        (nType != ScContentId::AREALINK) )
1258
0
    {
1259
0
        OUString aText(m_xTreeView->get_text(*xEntry));
1260
1261
0
        ScDocument* pLocalDoc = nullptr;                   // for URL drop
1262
0
        OUString aDocName;
1263
0
        ScDocShell* pDocSh = GetManualOrCurrent();
1264
0
        if (pDocSh)
1265
0
        {
1266
0
            if (pDocSh->HasName())
1267
0
                aDocName = pDocSh->GetMedium()->GetName();
1268
0
            else
1269
0
                pLocalDoc = &pDocSh->GetDocument();      // drop only in this document
1270
0
        }
1271
1272
0
        bool bDoLinkTrans = false;      // use ScLinkTransferObj
1273
0
        OUString aLinkURL;                // for ScLinkTransferObj
1274
0
        OUString aLinkText;
1275
1276
0
        sal_uInt16 nDropMode = pParentWindow->GetDropMode();
1277
0
        switch ( nDropMode )
1278
0
        {
1279
0
            case SC_DROPMODE_URL:
1280
0
                {
1281
0
                    OUString aUrl = aDocName + "#" + aText;
1282
1283
0
                    pScMod->SetDragJump( pLocalDoc, aUrl, aText );
1284
1285
0
                    if (!aDocName.isEmpty())
1286
0
                    {
1287
                        //  provide URL to outside only if the document has a name
1288
                        //  (without name, only internal D&D via SetDragJump)
1289
1290
0
                        aLinkURL = aUrl;
1291
0
                        aLinkText = aText;
1292
0
                    }
1293
0
                    bDoLinkTrans = true;
1294
0
                }
1295
0
                break;
1296
0
            case SC_DROPMODE_LINK:
1297
0
                {
1298
0
                    if ( !aDocName.isEmpty() )           // link only to named documents
1299
0
                    {
1300
                        // for internal D&D, set flag to insert a link
1301
1302
0
                        switch ( nType )
1303
0
                        {
1304
0
                            case ScContentId::TABLE:
1305
0
                                pScMod->SetDragLink( aDocName, aText, OUString() );
1306
0
                                bDoLinkTrans = true;
1307
0
                                break;
1308
0
                            case ScContentId::RANGENAME:
1309
0
                            case ScContentId::DBAREA:
1310
0
                                pScMod->SetDragLink( aDocName, OUString(), aText );
1311
0
                                bDoLinkTrans = true;
1312
0
                                break;
1313
1314
                            // other types cannot be linked
1315
0
                            default: break;
1316
0
                        }
1317
0
                    }
1318
0
                }
1319
0
                break;
1320
0
            case SC_DROPMODE_COPY:
1321
0
                {
1322
0
                    ScDocShell* pSrcShell = GetManualOrCurrent();
1323
0
                    if ( pSrcShell )
1324
0
                    {
1325
0
                        ScDocument& rSrcDoc = pSrcShell->GetDocument();
1326
0
                        if ( nType == ScContentId::RANGENAME || nType == ScContentId::DBAREA )
1327
0
                        {
1328
0
                            ScRange aRange;
1329
0
                            if ( lcl_GetRange( rSrcDoc, nType, aText, aRange ) )
1330
0
                            {
1331
0
                                bDisallow = lcl_DoDragCells( *pSrcShell, aRange, ScDragSrc::Navigator, *m_xTreeView );
1332
0
                            }
1333
0
                        }
1334
0
                        else if ( nType == ScContentId::TABLE )
1335
0
                        {
1336
0
                            SCTAB nTab;
1337
0
                            if ( rSrcDoc.GetTable( aText, nTab ) )
1338
0
                            {
1339
0
                                ScRange aRange(0, 0, nTab, rSrcDoc.MaxCol(), rSrcDoc.MaxRow(), nTab);
1340
0
                                bDisallow = lcl_DoDragCells( *pSrcShell, aRange, (ScDragSrc::Navigator | ScDragSrc::Table), *m_xTreeView );
1341
0
                            }
1342
0
                        }
1343
0
                        else if ( nType == ScContentId::GRAPHIC || nType == ScContentId::OLEOBJECT ||
1344
0
                                    nType == ScContentId::DRAWING )
1345
0
                        {
1346
0
                            bDisallow = lcl_DoDragObject( *pSrcShell, aText, nType, *m_xTreeView );
1347
1348
                            //  during ExecuteDrag the navigator can be deleted
1349
                            //  -> don't access member anymore !!!
1350
0
                        }
1351
0
                    }
1352
0
                }
1353
0
                break;
1354
0
        }
1355
1356
0
        if (bDoLinkTrans)
1357
0
        {
1358
0
            if (!aLinkURL.isEmpty())
1359
0
                m_xTransferObj->SetLinkURL(aLinkURL, aLinkText);
1360
1361
0
            rtl::Reference<TransferDataContainer> xHelper(m_xTransferObj);
1362
0
            m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
1363
1364
0
            bDisallow = false;
1365
0
        }
1366
0
    }
1367
1368
0
    return bDisallow;
1369
0
}
1370
1371
void ScContentTree::SetRootType( ScContentId nNew )
1372
0
{
1373
0
    if ( nNew != nRootType )
1374
0
    {
1375
0
        nRootType = nNew;
1376
0
        Refresh();
1377
1378
0
        ScNavipiCfg& rCfg = ScModule::get()->GetNavipiCfg();
1379
0
        rCfg.SetRootType( nRootType );
1380
0
    }
1381
0
}
1382
1383
void ScContentTree::ToggleRoot()        // after selection
1384
0
{
1385
0
    ScContentId nNew = ScContentId::ROOT;
1386
0
    if ( nRootType == ScContentId::ROOT )
1387
0
    {
1388
0
        if (std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->get_cursor())
1389
0
        {
1390
0
            std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(xEntry.get()));
1391
0
            if (!m_xTreeView->iter_parent(*xParent))
1392
0
                xParent.reset();
1393
1394
0
            for (sal_uInt16 i=1; i<=int(ScContentId::LAST); i++)
1395
0
            {
1396
0
                if (!m_aRootNodes[static_cast<ScContentId>(i)])
1397
0
                    continue;
1398
0
                if (xEntry->equal(*m_aRootNodes[static_cast<ScContentId>(i)]) ||
1399
0
                    (xParent && xParent->equal(*m_aRootNodes[static_cast<ScContentId>(i)])))
1400
0
                {
1401
0
                    nNew = static_cast<ScContentId>(i);
1402
0
                }
1403
0
            }
1404
0
        }
1405
0
    }
1406
1407
0
    SetRootType( nNew );
1408
0
}
1409
1410
void ScContentTree::ResetManualDoc()
1411
0
{
1412
0
    aManualDoc.clear();
1413
1414
0
    ActiveDocChanged();
1415
0
}
1416
1417
bool ScContentTree::ActiveDocChanged()
1418
0
{
1419
0
    bool bRefreshed = false;
1420
1421
0
    if (aManualDoc.isEmpty())
1422
0
    {
1423
0
        Refresh();                                  // content only if automatic
1424
0
        bRefreshed = true;
1425
0
    }
1426
1427
        //  if flag active Listbox must be updated
1428
1429
0
    OUString aCurrent;
1430
1431
0
    ScDocShell* pSh = GetManualOrCurrent();
1432
0
    if (pSh)
1433
0
        aCurrent = pSh->GetTitle();
1434
0
    else
1435
0
    {
1436
        //  document is no longer available
1437
1438
0
        aManualDoc.clear();             // again automatically
1439
0
        Refresh();
1440
0
        bRefreshed = true;
1441
0
        pSh = GetManualOrCurrent();     // should be active now
1442
0
        if (pSh)
1443
0
            aCurrent = pSh->GetTitle();
1444
0
    }
1445
1446
0
    pParentWindow->GetDocNames( &aCurrent );        // select
1447
1448
0
    return bRefreshed;
1449
0
}
1450
1451
void ScContentTree::SetManualDoc(const OUString& rName)
1452
0
{
1453
0
    aManualDoc = rName;
1454
0
    Refresh();
1455
0
    pParentWindow->GetDocNames( &aManualDoc );      // select
1456
0
}
1457
1458
void ScContentTree::SelectDoc(const OUString& rName)      // rName like shown in Menu/Listbox
1459
0
{
1460
0
    if ( rName == pParentWindow->aStrActiveWin )
1461
0
    {
1462
0
        ResetManualDoc();
1463
0
        return;
1464
0
    }
1465
1466
    //  omit "active" or "inactive"
1467
1468
0
    OUString aRealName = rName;
1469
0
    sal_Int32 nLen = rName.getLength();
1470
0
    sal_Int32 nActiveStart = nLen - pParentWindow->aStrActive.getLength();
1471
0
    if ( rName.subView( nActiveStart ) == pParentWindow->aStrActive )
1472
0
        aRealName = rName.copy( 0, nActiveStart );
1473
0
    sal_Int32 nNotActiveStart = nLen - pParentWindow->aStrNotActive.getLength();
1474
0
    if ( rName.subView( nNotActiveStart ) == pParentWindow->aStrNotActive )
1475
0
        aRealName = rName.copy( 0, nNotActiveStart );
1476
1477
0
    bool bLoaded = false;
1478
1479
    // Is it a normally loaded document?
1480
1481
0
    SfxObjectShell* pSh = SfxObjectShell::GetFirst();
1482
0
    while ( pSh && !bLoaded )
1483
0
    {
1484
0
        if ( dynamic_cast<const ScDocShell*>( pSh) !=  nullptr )
1485
0
            if ( pSh->GetTitle() == aRealName )
1486
0
                bLoaded = true;
1487
0
        pSh = SfxObjectShell::GetNext( *pSh );
1488
0
    }
1489
1490
0
    if (bLoaded)
1491
0
    {
1492
0
        SetManualDoc(aRealName);
1493
0
    }
1494
0
    else
1495
0
    {
1496
0
        OSL_FAIL("SelectDoc: not found");
1497
0
    }
1498
0
}
1499
1500
void ScContentTree::SelectEntryByName(const ScContentId nRoot, std::u16string_view rName)
1501
0
{
1502
0
    weld::TreeIter* pParent = m_aRootNodes[nRoot].get();
1503
1504
0
    if (!pParent || !m_xTreeView->iter_has_child(*pParent))
1505
0
        return;
1506
1507
0
    std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pParent));
1508
0
    bool bEntry = m_xTreeView->iter_children(*xEntry);
1509
1510
0
    while (bEntry)
1511
0
    {
1512
0
        if (m_xTreeView->get_text(*xEntry) == rName)
1513
0
        {
1514
0
            m_xTreeView->select(*xEntry);
1515
0
            m_xTreeView->set_cursor(*xEntry);
1516
1517
            // Scroll to the selected item
1518
0
            m_xTreeView->scroll_to_row(*xEntry);
1519
1520
0
            StoreNavigatorSettings();
1521
1522
0
            return;
1523
0
        }
1524
0
        bEntry = m_xTreeView->iter_next(*xEntry);
1525
0
    }
1526
0
}
1527
1528
void ScContentTree::BringCommentToAttention(sal_uInt16 nCommentId)
1529
0
{
1530
0
    std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
1531
0
    if (!m_xTreeView->get_iter_first(*xIter))
1532
0
        return;
1533
1534
0
    do
1535
0
    {
1536
0
        ScContentId nType;
1537
0
        sal_uLong nChild;
1538
0
        GetEntryIndexes(nType, nChild, xIter.get());
1539
1540
0
        if (nType == ScContentId::NOTE)
1541
0
        {
1542
0
            m_xTreeView->set_cursor(*xIter);
1543
0
            m_xTreeView->select(*xIter);
1544
0
            m_xTreeView->expand_row(*xIter);
1545
1546
0
            OUString aCommentId(OUString::number(nCommentId));
1547
0
            for (bool bChild = m_xTreeView->iter_children(*xIter); bChild;
1548
0
                 bChild = m_xTreeView->iter_next_sibling(*xIter))
1549
0
            {
1550
0
                if (m_xTreeView->get_id(*xIter) == aCommentId)
1551
0
                {
1552
0
                    m_xTreeView->select(*xIter);
1553
0
                    break;
1554
0
                }
1555
0
            }
1556
0
            break;
1557
0
        }
1558
0
        else
1559
0
            m_xTreeView->collapse_row(*xIter);
1560
1561
0
    } while (m_xTreeView->iter_next_sibling(*xIter));}
1562
1563
void ScContentTree::ApplyNavigatorSettings()
1564
0
{
1565
0
    const ScNavigatorSettings* pSettings = ScNavigatorDlg::GetNavigatorSettings();
1566
0
    if( !pSettings )
1567
0
        return;
1568
1569
0
    ScContentId nRootSel = pSettings->GetRootSelected();
1570
0
    auto nChildSel = pSettings->GetChildSelected();
1571
1572
    // tdf#133079 ensure Sheet root is selected if nothing
1573
    // else would be
1574
0
    if (nRootSel == ScContentId::ROOT)
1575
0
    {
1576
0
        nRootSel = ScContentId::TABLE;
1577
0
        nChildSel = SC_CONTENT_NOCHILD;
1578
0
    }
1579
1580
0
    for( int i = 1; i <= int(ScContentId::LAST); ++i )
1581
0
    {
1582
0
        ScContentId nEntry = static_cast<ScContentId>(i);
1583
0
        if( m_aRootNodes[ nEntry ] )
1584
0
        {
1585
            // gray or ungray
1586
0
            if (!m_xTreeView->iter_has_child(*m_aRootNodes[nEntry]))
1587
0
                m_xTreeView->set_sensitive(*m_aRootNodes[nEntry], false);
1588
0
            else
1589
0
                m_xTreeView->set_sensitive(*m_aRootNodes[nEntry], true);
1590
1591
            // expand
1592
0
            bool bExp = pSettings->IsExpanded( nEntry );
1593
0
            if (bExp != m_xTreeView->get_row_expanded(*m_aRootNodes[nEntry]))
1594
0
            {
1595
0
                if( bExp )
1596
0
                    m_xTreeView->expand_row(*m_aRootNodes[nEntry]);
1597
0
                else
1598
0
                    m_xTreeView->collapse_row(*m_aRootNodes[nEntry]);
1599
0
            }
1600
1601
            // select
1602
0
            if( nRootSel == nEntry )
1603
0
            {
1604
0
                std::unique_ptr<weld::TreeIter> xEntry;
1605
0
                if (bExp && (nChildSel != SC_CONTENT_NOCHILD))
1606
0
                {
1607
0
                    xEntry = m_xTreeView->make_iterator(m_aRootNodes[nEntry].get());
1608
0
                    if (!m_xTreeView->iter_children(*xEntry) || !m_xTreeView->iter_nth_sibling(*xEntry, nChildSel))
1609
0
                        xEntry.reset();
1610
0
                }
1611
0
                m_xTreeView->select(xEntry ? *xEntry : *m_aRootNodes[nEntry]);
1612
0
                m_xTreeView->set_cursor(xEntry ? *xEntry : *m_aRootNodes[nEntry]);
1613
0
            }
1614
0
        }
1615
0
    }
1616
0
}
1617
1618
void ScContentTree::StoreNavigatorSettings()
1619
0
{
1620
0
    if (m_nAsyncMouseReleaseId)
1621
0
    {
1622
0
        Application::RemoveUserEvent(m_nAsyncMouseReleaseId);
1623
0
        m_nAsyncMouseReleaseId = nullptr;
1624
0
    }
1625
1626
0
    ScNavigatorSettings* pSettings = ScNavigatorDlg::GetNavigatorSettings();
1627
0
    if( !pSettings )
1628
0
        return;
1629
1630
0
    for( int i = 1; i <= int(ScContentId::LAST); ++i )
1631
0
    {
1632
0
        ScContentId nEntry = static_cast<ScContentId>(i);
1633
0
        bool bExp = m_aRootNodes[nEntry] && m_xTreeView->get_row_expanded(*m_aRootNodes[nEntry]);
1634
0
        pSettings->SetExpanded( nEntry, bExp );
1635
0
    }
1636
1637
0
    std::unique_ptr<weld::TreeIter> xCurEntry = m_xTreeView->get_cursor();
1638
1639
0
    ScContentId nRoot;
1640
0
    sal_uLong nChild;
1641
0
    GetEntryIndexes(nRoot, nChild, xCurEntry.get());
1642
1643
0
    pSettings->SetRootSelected( nRoot );
1644
0
    pSettings->SetChildSelected( nChild );
1645
0
}
1646
1647
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */