    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <tools/urlobj.hxx>
    #include <vcl/dialog.hxx>
    #include <vcl/button.hxx>
    #include <vcl/fixed.hxx>
    #include <vcl/lstbox.hxx>
    #include <vcl/weld.hxx>
    #include <svl/fstathelper.hxx>
    #include <unotools/pathoptions.hxx>
    #include <unotools/transliterationwrapper.hxx>
    #include <swtypes.hxx>
    #include <swmodule.hxx>
    #include <shellio.hxx>
    #include <initui.hxx>
    #include <glosdoc.hxx>
    #include <gloslst.hxx>
    #include <swunohelper.hxx>
    #include <view.hxx>

    #include <vector>

    #define STRING_DELIM char(0x0A)
    #define GLOS_TIMEOUT 30000   // update every 30 seconds
    #define FIND_MAX_GLOS 20

    struct TripleString
    {
        OUString sGroup;
        OUString sBlock;
        OUString sShort;
    };

    class SwGlossDecideDlg : public weld::GenericDialogController
    {
        std::unique_ptr<weld::Button> m_xOk;
        std::unique_ptr<weld::TreeView> m_xListLB;

        DECL_LINK(DoubleClickHdl, weld::TreeView&, void);
        DECL_LINK(SelectHdl, weld::TreeView&, void);

    public:
        explicit SwGlossDecideDlg(weld::Window* pParent);

        weld::TreeView& GetTreeView() {return *m_xListLB;}
    };

    SwGlossDecideDlg::SwGlossDecideDlg(weld::Window* pParent)
        : GenericDialogController(pParent, "modules/swriter/ui/selectautotextdialog.ui", "SelectAutoTextDialog")
        , m_xOk(m_xBuilder->weld_button("ok"))
        , m_xListLB(m_xBuilder->weld_tree_view("treeview"))
    {
        m_xListLB->set_size_request(m_xListLB->get_approximate_digit_width() * 32,
                                    m_xListLB->get_height_rows(8));
        m_xListLB->connect_row_activated(LINK(this, SwGlossDecideDlg, DoubleClickHdl));
        m_xListLB->connect_changed(LINK(this, SwGlossDecideDlg, SelectHdl));
    }

    IMPL_LINK_NOARG(SwGlossDecideDlg, DoubleClickHdl, weld::TreeView&, void)
    {
        m_xDialog->response(RET_OK);
    }

    IMPL_LINK_NOARG(SwGlossDecideDlg, SelectHdl, weld::TreeView&, void)
    {
        m_xOk->set_sensitive(m_xListLB->get_selected_index() != -1);
    }

    SwGlossaryList::SwGlossaryList() :
        bFilled(false)
    {
        SvtPathOptions aPathOpt;
        sPath = aPathOpt.GetAutoTextPath();
        SetTimeout(GLOS_TIMEOUT);
    }

    SwGlossaryList::~SwGlossaryList()
    {
        ClearGroups();
    }

    // If the GroupName is already known, then only rShortName
    // will be filled. Otherwise also rGroupName will be set and
    // on demand asked for the right group.

    bool SwGlossaryList::GetShortName(const OUString& rLongName,
                                      OUString& rShortName, OUString& rGroupName )
    {
        if(!bFilled)
            Update();

        std::vector<TripleString> aTripleStrings;

        size_t nCount = aGroupArr.size();
        for(size_t i = 0; i < nCount; i++ )
        {
            AutoTextGroup* pGroup = aGroupArr[i].get();
            if(!rGroupName.isEmpty() && rGroupName != pGroup->sName)
                continue;

            sal_Int32 nPosLong = 0;
            for(sal_uInt16 j = 0; j < pGroup->nCount; j++)
            {
                const OUString sLong = pGroup->sLongNames.getToken(0, STRING_DELIM, nPosLong);
                if(rLongName != sLong)
                    continue;

                TripleString aTriple;
                aTriple.sGroup = pGroup->sName;
                aTriple.sBlock = sLong;
                aTriple.sShort = pGroup->sShortNames.getToken(j, STRING_DELIM);
                aTripleStrings.push_back(aTriple);
            }
        }

        bool bRet = false;
        nCount = aTripleStrings.size();
        if(1 == nCount)
        {
            const TripleString& rTriple(aTripleStrings.front());
            rShortName = rTriple.sShort;
            rGroupName = rTriple.sGroup;
            bRet = true;
        }
        else if(1 < nCount)
        {
            SwView *pView  = ::GetActiveView();
            SwGlossDecideDlg aDlg(pView ? pView->GetFrameWeld() : nullptr);
            OUString sTitle = aDlg.get_title() + " " + aTripleStrings.front().sBlock;
            aDlg.set_title(sTitle);

            weld::TreeView& rLB = aDlg.GetTreeView();
            for(std::vector<TripleString>::const_iterator i = aTripleStrings.begin(); i != aTripleStrings.end(); ++i)
                rLB.append_text(i->sGroup.getToken(0, GLOS_DELIM));

            rLB.select(0);
            if (aDlg.run() == RET_OK && rLB.get_selected_index() != -1)
            {
                const TripleString& rTriple(aTripleStrings[rLB.get_selected_index()]);
                rShortName = rTriple.sShort;
                rGroupName = rTriple.sGroup;
                bRet = true;
            }
            else
                bRet = false;
        }
        return bRet;
    }

    size_t SwGlossaryList::GetGroupCount()
    {
        if(!bFilled)
            Update();
        return aGroupArr.size();
    }

    OUString SwGlossaryList::GetGroupName(size_t nPos)
    {
        OSL_ENSURE(aGroupArr.size() > nPos, "group not available");
        if(nPos < aGroupArr.size())
        {
            AutoTextGroup* pGroup = aGroupArr[nPos].get();
            OUString sRet = pGroup->sName;
            return sRet;
        }
        return OUString();
    }

    OUString SwGlossaryList::GetGroupTitle(size_t nPos)
    {
        OSL_ENSURE(aGroupArr.size() > nPos, "group not available");
        if(nPos < aGroupArr.size())
        {
            AutoTextGroup* pGroup = aGroupArr[nPos].get();
            return pGroup->sTitle;
        }
        return OUString();
    }

    sal_uInt16 SwGlossaryList::GetBlockCount(size_t nGroup)
    {
        OSL_ENSURE(aGroupArr.size() > nGroup, "group not available");
        if(nGroup < aGroupArr.size())
        {
            AutoTextGroup* pGroup = aGroupArr[nGroup].get();
            return pGroup->nCount;
        }
        return 0;
    }

    OUString SwGlossaryList::GetBlockLongName(size_t nGroup, sal_uInt16 nBlock)
    {
        OSL_ENSURE(aGroupArr.size() > nGroup, "group not available");
        if(nGroup < aGroupArr.size())
        {
            AutoTextGroup* pGroup = aGroupArr[nGroup].get();
            return pGroup->sLongNames.getToken(nBlock, STRING_DELIM);
        }
        return OUString();
    }

    OUString SwGlossaryList::GetBlockShortName(size_t nGroup, sal_uInt16 nBlock)
    {
        OSL_ENSURE(aGroupArr.size() > nGroup, "group not available");
        if(nGroup < aGroupArr.size())
        {
            AutoTextGroup* pGroup = aGroupArr[nGroup].get();
            return pGroup->sShortNames.getToken(nBlock, STRING_DELIM);
        }
        return OUString();
    }

    void SwGlossaryList::Update()
    {
        if(!IsActive())
            Start();

        SvtPathOptions aPathOpt;
        OUString sTemp( aPathOpt.GetAutoTextPath() );
        if(sTemp != sPath)
        {
            sPath = sTemp;
            bFilled = false;
            ClearGroups();
        }
        SwGlossaries* pGlossaries = ::GetGlossaries();
        const std::vector<OUString> & rPathArr = pGlossaries->GetPathArray();
        const OUString sExt( SwGlossaries::GetExtension() );
        if(!bFilled)
        {
            const size_t nGroupCount = pGlossaries->GetGroupCnt();
            for(size_t i = 0; i < nGroupCount; ++i)
            {
                OUString sGrpName = pGlossaries->GetGroupName(i);
                const size_t nPath = static_cast<size_t>(
                    sGrpName.getToken(1, GLOS_DELIM).toInt32());
                if( nPath < rPathArr.size() )
                {
                    std::unique_ptr<AutoTextGroup> pGroup(new AutoTextGroup);
                    pGroup->sName = sGrpName;

                    FillGroup(pGroup.get(), pGlossaries);
                    OUString sName = rPathArr[nPath] + "/" +
                        pGroup->sName.getToken(0, GLOS_DELIM) + sExt;
                    FStatHelper::GetModifiedDateTimeOfFile( sName,
                                                    &pGroup->aDateModified,
                                                    &pGroup->aDateModified );

                    aGroupArr.insert( aGroupArr.begin(), std::move(pGroup) );
                }
            }
            bFilled = true;
        }
        else
        {
            for( size_t nPath = 0; nPath < rPathArr.size(); nPath++ )
            {
                std::vector<OUString> aFoundGroupNames;
                std::vector<OUString> aFiles;
                std::vector<DateTime*> aDateTimeArr;

                SWUnoHelper::UCB_GetFileListOfFolder( rPathArr[nPath], aFiles,
                                                        &sExt, &aDateTimeArr );
                for( size_t nFiles = 0; nFiles < aFiles.size(); ++nFiles )
                {
                    const OUString aTitle = aFiles[ nFiles ];
                    ::DateTime* pDT = aDateTimeArr[ nFiles ];

                    OUString sName( aTitle.copy( 0, aTitle.getLength() - sExt.getLength() ));

                    aFoundGroupNames.push_back(sName);
                    sName += OUStringLiteral1(GLOS_DELIM) + OUString::number( static_cast<sal_uInt16>(nPath) );
                    AutoTextGroup* pFound = FindGroup( sName );
                    if( !pFound )
                    {
                        pFound = new AutoTextGroup;
                        pFound->sName = sName;
                        FillGroup( pFound, pGlossaries );
                        pFound->aDateModified = *pDT;

                        aGroupArr.push_back(std::unique_ptr<AutoTextGroup>(pFound));
                    }
                    else if( pFound->aDateModified < *pDT )
                    {
                        FillGroup(pFound, pGlossaries);
                        pFound->aDateModified = *pDT;
                    }

                    // don't need any more these pointers
                    delete pDT;
                }

                for( size_t i = aGroupArr.size(); i>0; )
                {
                    --i;
                    // maybe remove deleted groups
                    AutoTextGroup* pGroup = aGroupArr[i].get();
                    const size_t nGroupPath = static_cast<size_t>(
                        pGroup->sName.getToken( 1, GLOS_DELIM).toInt32());
                    // Only the groups will be checked which are registered
                    // for the current subpath.
                    if( nGroupPath == nPath )
                    {
                        bool bFound = false;
                        OUString sCompareGroup = pGroup->sName.getToken(0, GLOS_DELIM);
                        for(std::vector<OUString>::const_iterator j = aFoundGroupNames.begin(); j != aFoundGroupNames.end() && !bFound; ++j)
                            bFound = (sCompareGroup == *j);

                        if(!bFound)
                        {
                            aGroupArr.erase(aGroupArr.begin() + i);
                            delete pGroup;
                        }
                    }
                }
            }
        }
    }

    void SwGlossaryList::Invoke()
    {
        // Only update automatically if a SwView has the focus.
        if(::GetActiveView())
            Update();
    }

    AutoTextGroup* SwGlossaryList::FindGroup(const OUString& rGroupName)
    {
        for(auto & pRet : aGroupArr)
        {
            if(pRet->sName == rGroupName)
                return pRet.get();
        }
        return nullptr;
    }

    void SwGlossaryList::FillGroup(AutoTextGroup* pGroup, SwGlossaries* pGlossaries)
    {
        SwTextBlocks*   pBlock = pGlossaries->GetGroupDoc(pGroup->sName);
        pGroup->nCount = pBlock ? pBlock->GetCount() : 0;
        pGroup->sLongNames.clear();
        pGroup->sShortNames.clear();
        if(pBlock)
            pGroup->sTitle = pBlock->GetName();

        for(sal_uInt16 j = 0; j < pGroup->nCount; j++)
        {
            pGroup->sLongNames += pBlock->GetLongName(j)
                + OUStringLiteral1(STRING_DELIM);
            pGroup->sShortNames += pBlock->GetShortName(j)
                + OUStringLiteral1(STRING_DELIM);
        }
        delete pBlock;
    }

    // Give back all (not exceeding FIND_MAX_GLOS) found modules
    // with matching beginning.

    void SwGlossaryList::HasLongName(const OUString& rBegin, std::vector<OUString> *pLongNames)
    {
        if(!bFilled)
            Update();
        sal_uInt16 nFound = 0;
        const size_t nCount = aGroupArr.size();
        sal_Int32 nBeginLen = rBegin.getLength();
        const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();

        for(size_t i = 0; i < nCount; ++i)
        {
            AutoTextGroup* pGroup = aGroupArr[i].get();
            for(sal_uInt16 j = 0; j < pGroup->nCount; j++)
            {
                OUString sBlock = pGroup->sLongNames.getToken(j, STRING_DELIM);
                if( nBeginLen + 1 < sBlock.getLength() &&
                    rSCmp.isEqual( sBlock.copy(0, nBeginLen), rBegin ))
                {
                    pLongNames->push_back( sBlock );
                    nFound++;
                    if(FIND_MAX_GLOS == nFound)
                        break;
                }
            }
        }
    }

    void    SwGlossaryList::ClearGroups()
    {
        aGroupArr.clear();
        bFilled = false;
    }

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V586 The 'delete' operator is called twice for deallocation of the same memory space.