/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bitmaps.hlst" #include "swabstdlg.hxx" #include using namespace ::com::sun::star::uno; // Context menu for GlobalTree #define CTX_INSERT_ANY_INDEX 10 #define CTX_INSERT_FILE 11 #define CTX_INSERT_NEW_FILE 12 #define CTX_INSERT_TEXT 13 #define CTX_UPDATE_SEL 20 #define CTX_UPDATE_INDEX 21 #define CTX_UPDATE_LINK 22 #define CTX_UPDATE_ALL 23 #define CTX_UPDATE 1 #define CTX_INSERT 2 #define CTX_EDIT 3 #define CTX_DELETE 4 #define CTX_EDIT_LINK 5 #define GLOBAL_UPDATE_TIMEOUT 2000 // TabPos: push to left #define GLBL_TABPOS_SUB 5 const SfxObjectShell* SwGlobalTree::pShowShell = nullptr; static const char* aHelpForMenu[] = { nullptr, HID_GLBLTREE_UPDATE, //CTX_UPDATE HID_GLBLTREE_INSERT, //CTX_INSERT HID_GLBLTREE_EDIT, //CTX_EDIT HID_GLBLTREE_DEL, //CTX_DELETE HID_GLBLTREE_EDIT_LINK, //CTX_EDIT_LINK nullptr, nullptr, nullptr, nullptr, HID_GLBLTREE_INS_IDX, //CTX_INSERT_ANY_INDEX HID_GLBLTREE_INS_FILE, //CTX_INSERT_FILE HID_GLBLTREE_INS_NEW_FILE, //CTX_INSERT_NEW_FILE HID_GLBLTREE_INS_TEXT, //CTX_INSERT_TEXT nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, HID_GLBLTREE_UPD_SEL, //CTX_UPDATE_SEL HID_GLBLTREE_UPD_IDX, //CTX_UPDATE_INDEX HID_GLBLTREE_UPD_LINK, //CTX_UPDATE_LINK HID_GLBLTREEUPD_ALL //CTX_UPDATE_ALL }; class SwGlobalFrameListener_Impl : public SfxListener { bool bValid; public: explicit SwGlobalFrameListener_Impl(SfxViewFrame& rFrame) : bValid(true) { StartListening(rFrame); } virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; bool IsValid() const {return bValid;} }; void SwGlobalFrameListener_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) { if( rHint.GetId() == SfxHintId::Dying) bValid = false; } SwGlobalTree::SwGlobalTree(vcl::Window* pParent, SwNavigationPI* pDialog) : SvTreeListBox(pParent) , m_xDialog(pDialog) , m_pActiveShell(nullptr) , m_pEmphasisEntry(nullptr) , m_pDDSource(nullptr) , m_pSwGlblDocContents(nullptr) , m_pDocContent(nullptr) , m_pDocInserter(nullptr) , m_bIsInternalDrag(false) , m_bLastEntryEmphasis(false) { SetDragDropMode(DragDropMode::APP_COPY | DragDropMode::CTRL_MOVE | DragDropMode::ENABLE_TOP ); m_aUpdateTimer.SetTimeout(GLOBAL_UPDATE_TIMEOUT); m_aUpdateTimer.SetInvokeHandler(LINK(this, SwGlobalTree, Timeout)); m_aUpdateTimer.Start(); for(sal_uInt16 i = 0; i < GLOBAL_CONTEXT_COUNT; i++) { m_aContextStrings[i] = SwResId(i+ STR_GLOBAL_CONTEXT_FIRST); } SetHelpId(HID_NAVIGATOR_GLOB_TREELIST); SelectHdl(); SetDoubleClickHdl(LINK(this, SwGlobalTree, DoubleClickHdl)); EnableContextMenuHandling(); } SwGlobalTree::~SwGlobalTree() { disposeOnce(); } void SwGlobalTree::dispose() { delete m_pSwGlblDocContents; m_pSwGlblDocContents = nullptr; delete m_pDocInserter; m_pDocInserter = nullptr; m_aUpdateTimer.Stop(); m_xDialog.clear(); SvTreeListBox::dispose(); } Size SwGlobalTree::GetOptimalSize() const { return LogicToPixel(Size(110, 112), MapUnit::MapAppFont); } sal_Int8 SwGlobalTree::ExecuteDrop( const ExecuteDropEvent& rEvt ) { sal_Int8 nRet = DND_ACTION_NONE; SvTreeListEntry* pLast = LastVisible(); if(m_pEmphasisEntry) { ImplShowTargetEmphasis( Prev(m_pEmphasisEntry), false ); m_pEmphasisEntry = nullptr; } else if(m_bLastEntryEmphasis && pLast) { ImplShowTargetEmphasis( pLast, false); } SvTreeListEntry* pDropEntry = m_bLastEntryEmphasis ? nullptr : GetEntry(rEvt.maPosPixel); if( m_bIsInternalDrag ) { SvTreeListEntry* pDummy = nullptr; sal_uLong nInsertionPos = TREELIST_APPEND; NotifyMoving( pDropEntry, m_pDDSource, pDummy, nInsertionPos ); } else { TransferableDataHelper aData( rEvt.maDropEvent.Transferable ); OUString sFileName; const SwGlblDocContent* pCnt = pDropEntry ? static_cast(pDropEntry->GetUserData()) : nullptr; if( aData.HasFormat( SotClipboardFormatId::FILE_LIST )) { nRet = rEvt.mnAction; std::unique_ptr pTempContents(new SwGlblDocContents); int nAbsContPos = pDropEntry ? (int) GetModel()->GetAbsPos(pDropEntry): - 1; sal_uLong nEntryCount = GetEntryCount(); // Get data FileList aFileList; aData.GetFileList( SotClipboardFormatId::FILE_LIST, aFileList ); for ( size_t n = aFileList.Count(); n--; ) { sFileName = aFileList.GetFile(n); InsertRegion(pCnt, &sFileName); // The list of contents must be newly fetched after inserting, // to not work on an old content. if(n) { m_pActiveShell->GetGlobalDocContent(*pTempContents); // If the file was successfully inserted, // then the next content must also be fetched. if(nEntryCount < pTempContents->size()) { nEntryCount++; nAbsContPos++; pCnt = (*pTempContents)[ nAbsContPos ]; } } } } else if( !(sFileName = SwNavigationPI::CreateDropFileName( aData )).isEmpty()) { INetURLObject aTemp(sFileName); GraphicDescriptor aDesc(aTemp); if( !aDesc.Detect() ) // accept no graphics { nRet = rEvt.mnAction; InsertRegion(pCnt, &sFileName); } } } m_bLastEntryEmphasis = false; return nRet; } sal_Int8 SwGlobalTree::AcceptDrop( const AcceptDropEvent& rEvt ) { sal_Int8 nRet = rEvt.mnAction; //initiate scrolling GetDropTarget( rEvt.maPosPixel ); SvTreeListEntry* pLast = LastVisible(); if( rEvt.mbLeaving ) { if( m_pEmphasisEntry ) { ImplShowTargetEmphasis( Prev(m_pEmphasisEntry), false ); m_pEmphasisEntry = nullptr; } else if(m_bLastEntryEmphasis && pLast) { ImplShowTargetEmphasis( pLast, false); } m_bLastEntryEmphasis = false; } else { SvTreeListEntry* pDropEntry = GetEntry( rEvt.maPosPixel ); if(m_bIsInternalDrag) { if( m_pDDSource != pDropEntry ) nRet = rEvt.mnAction; } else if( IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) || IsDropFormatSupported( SotClipboardFormatId::STRING ) || IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) || IsDropFormatSupported( SotClipboardFormatId::SOLK ) || IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK )|| IsDropFormatSupported( SotClipboardFormatId::FILECONTENT ) || IsDropFormatSupported( SotClipboardFormatId::FILEGRPDESCRIPTOR ) || IsDropFormatSupported( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) || IsDropFormatSupported( SotClipboardFormatId::FILENAME )) nRet = DND_ACTION_LINK; if(m_pEmphasisEntry && m_pEmphasisEntry != pDropEntry) ImplShowTargetEmphasis( Prev(m_pEmphasisEntry), false ); else if(pLast && m_bLastEntryEmphasis && pDropEntry) { ImplShowTargetEmphasis( pLast, false); m_bLastEntryEmphasis = false; } if(pDropEntry) ImplShowTargetEmphasis( Prev(pDropEntry), DND_ACTION_NONE != nRet ); else if(pLast) { ImplShowTargetEmphasis( pLast, DND_ACTION_NONE != nRet ); m_bLastEntryEmphasis = true; } m_pEmphasisEntry = pDropEntry; } return nRet; } VclPtr SwGlobalTree::CreateContextMenu() { VclPtr pPop; if(m_pActiveShell && !m_pActiveShell->GetView().GetDocShell()->IsReadOnly()) { const MenuEnableFlags nEnableFlags = GetEnableFlags(); pPop = VclPtr::Create(); VclPtrInstance pSubPop1; VclPtrInstance pSubPop2; for (sal_uInt16 i = CTX_UPDATE_SEL; i <= CTX_UPDATE_ALL; i++) { pSubPop2->InsertItem( i, m_aContextStrings[STR_UPDATE_SEL - STR_GLOBAL_CONTEXT_FIRST - CTX_UPDATE_SEL+ i] ); pSubPop2->SetHelpId(i, aHelpForMenu[i]); } pSubPop2->EnableItem(CTX_UPDATE_SEL, bool(nEnableFlags & MenuEnableFlags::UpdateSel)); pSubPop1->InsertItem(CTX_INSERT_ANY_INDEX, m_aContextStrings[STR_INDEX - STR_GLOBAL_CONTEXT_FIRST]); pSubPop1->SetHelpId(CTX_INSERT_ANY_INDEX, aHelpForMenu[CTX_INSERT_ANY_INDEX]); pSubPop1->InsertItem(CTX_INSERT_FILE, m_aContextStrings[STR_FILE - STR_GLOBAL_CONTEXT_FIRST]); pSubPop1->SetHelpId(CTX_INSERT_FILE, aHelpForMenu[CTX_INSERT_FILE]); pSubPop1->InsertItem(CTX_INSERT_NEW_FILE, m_aContextStrings[STR_NEW_FILE - STR_GLOBAL_CONTEXT_FIRST]); pSubPop1->SetHelpId(CTX_INSERT_NEW_FILE, aHelpForMenu[CTX_INSERT_NEW_FILE]); pSubPop1->InsertItem(CTX_INSERT_TEXT, m_aContextStrings[STR_INSERT_TEXT - STR_GLOBAL_CONTEXT_FIRST]); pSubPop1->SetHelpId(CTX_INSERT_TEXT, aHelpForMenu[CTX_INSERT_TEXT]); pPop->InsertItem(CTX_UPDATE, m_aContextStrings[STR_UPDATE - STR_GLOBAL_CONTEXT_FIRST]); pPop->SetHelpId(CTX_UPDATE, aHelpForMenu[CTX_UPDATE]); pPop->InsertItem(CTX_EDIT, m_aContextStrings[STR_EDIT_CONTENT - STR_GLOBAL_CONTEXT_FIRST]); pPop->SetHelpId(CTX_EDIT, aHelpForMenu[CTX_EDIT]); if(nEnableFlags&MenuEnableFlags::EditLink) { pPop->InsertItem(CTX_EDIT_LINK, m_aContextStrings[STR_EDIT_LINK - STR_GLOBAL_CONTEXT_FIRST]); pPop->SetHelpId(CTX_EDIT_LINK, aHelpForMenu[CTX_EDIT_LINK]); } pPop->InsertItem(CTX_INSERT, m_aContextStrings[STR_EDIT_INSERT - STR_GLOBAL_CONTEXT_FIRST]); pPop->SetHelpId(CTX_INSERT, aHelpForMenu[CTX_INSERT]); pPop->InsertSeparator() ; pPop->InsertItem(CTX_DELETE, m_aContextStrings[STR_DELETE - STR_GLOBAL_CONTEXT_FIRST]); pPop->SetHelpId(CTX_DELETE, aHelpForMenu[CTX_DELETE]); //disabling if applicable pSubPop1->EnableItem(CTX_INSERT_ANY_INDEX, bool(nEnableFlags & MenuEnableFlags::InsertIdx )); pSubPop1->EnableItem(CTX_INSERT_TEXT, bool(nEnableFlags & MenuEnableFlags::InsertText)); pSubPop1->EnableItem(CTX_INSERT_FILE, bool(nEnableFlags & MenuEnableFlags::InsertFile)); pSubPop1->EnableItem(CTX_INSERT_NEW_FILE, bool(nEnableFlags & MenuEnableFlags::InsertFile)); pPop->EnableItem(CTX_UPDATE, bool(nEnableFlags & MenuEnableFlags::Update)); pPop->EnableItem(CTX_INSERT, bool(nEnableFlags & MenuEnableFlags::InsertIdx)); pPop->EnableItem(CTX_EDIT, bool(nEnableFlags & MenuEnableFlags::Edit)); pPop->EnableItem(CTX_DELETE, bool(nEnableFlags & MenuEnableFlags::Delete)); pPop->SetPopupMenu( CTX_INSERT, pSubPop1 ); pPop->SetPopupMenu( CTX_UPDATE, pSubPop2 ); } return pPop; } void SwGlobalTree::TbxMenuHdl(sal_uInt16 nTbxId, ToolBox* pBox) { const MenuEnableFlags nEnableFlags = GetEnableFlags(); const OUString sCommand(pBox->GetItemCommand(nTbxId)); if (sCommand == "insert") { ScopedVclPtrInstance pMenu; for (sal_uInt16 i = CTX_INSERT_ANY_INDEX; i <= CTX_INSERT_TEXT; ++i) { pMenu->InsertItem( i, m_aContextStrings[STR_INDEX - STR_GLOBAL_CONTEXT_FIRST - CTX_INSERT_ANY_INDEX + i] ); pMenu->SetHelpId(i, aHelpForMenu[i] ); } pMenu->EnableItem(CTX_INSERT_ANY_INDEX, bool(nEnableFlags & MenuEnableFlags::InsertIdx )); pMenu->EnableItem(CTX_INSERT_TEXT, bool(nEnableFlags & MenuEnableFlags::InsertText)); pMenu->EnableItem(CTX_INSERT_FILE, bool(nEnableFlags & MenuEnableFlags::InsertFile)); pMenu->EnableItem(CTX_INSERT_NEW_FILE, bool(nEnableFlags & MenuEnableFlags::InsertFile)); pMenu->SetSelectHdl(LINK(this, SwGlobalTree, PopupHdl)); pMenu->Execute(pBox, pBox->GetItemRect(nTbxId)); pMenu.disposeAndClear(); pBox->EndSelection(); pBox->Invalidate(); } else if (sCommand == "update") { ScopedVclPtrInstance pMenu; for (sal_uInt16 i = CTX_UPDATE_SEL; i <= CTX_UPDATE_ALL; i++) { pMenu->InsertItem( i, m_aContextStrings[STR_UPDATE_SEL - STR_GLOBAL_CONTEXT_FIRST - CTX_UPDATE_SEL+ i] ); pMenu->SetHelpId(i, aHelpForMenu[i] ); } pMenu->EnableItem(CTX_UPDATE_SEL, bool(nEnableFlags & MenuEnableFlags::UpdateSel)); pMenu->SetSelectHdl(LINK(this, SwGlobalTree, PopupHdl)); pMenu->Execute(pBox, pBox->GetItemRect(nTbxId)); pMenu.disposeAndClear(); pBox->EndSelection(); pBox->Invalidate(); } } MenuEnableFlags SwGlobalTree::GetEnableFlags() const { SvTreeListEntry* pEntry = FirstSelected(); sal_uLong nSelCount = GetSelectionCount(); sal_uLong nEntryCount = GetEntryCount(); SvTreeListEntry* pPrevEntry = pEntry ? Prev(pEntry) : nullptr; MenuEnableFlags nRet = MenuEnableFlags::NONE; if(nSelCount == 1 || !nEntryCount) nRet |= MenuEnableFlags::InsertIdx|MenuEnableFlags::InsertFile; if(nSelCount == 1) { nRet |= MenuEnableFlags::Edit; if (pEntry && static_cast(pEntry->GetUserData())->GetType() != GLBLDOC_UNKNOWN && (!pPrevEntry || static_cast(pPrevEntry->GetUserData())->GetType() != GLBLDOC_UNKNOWN)) nRet |= MenuEnableFlags::InsertText; if (pEntry && GLBLDOC_SECTION == static_cast(pEntry->GetUserData())->GetType()) nRet |= MenuEnableFlags::EditLink; } else if(!nEntryCount) { nRet |= MenuEnableFlags::InsertText; } if(nEntryCount) nRet |= MenuEnableFlags::Update|MenuEnableFlags::Delete; if(nSelCount) nRet |= MenuEnableFlags::UpdateSel; return nRet; } void SwGlobalTree::RequestHelp( const HelpEvent& rHEvt ) { bool bParent = true; Update(true); Display(true); if( rHEvt.GetMode() & HelpEventMode::QUICK ) { Point aPos( ScreenToOutputPixel( rHEvt.GetMousePosPixel() )); SvTreeListEntry* pEntry = GetEntry( aPos ); const SwGlblDocContent* pCont = pEntry ? static_cast(pEntry->GetUserData()) : nullptr; if( pCont && GLBLDOC_SECTION == pCont->GetType()) { bParent = false; SvLBoxTab* pTab; SvLBoxItem* pItem = GetItem( pEntry, aPos.X(), &pTab ); if (pItem && SvLBoxItemType::String == pItem->GetType()) { const SwSection* pSect = pCont->GetSection(); OUString sEntry = pSect->GetLinkFileName().getToken(0, sfx2::cTokenSeparator); if(!pSect->IsConnectFlag()) sEntry = m_aContextStrings[STR_BROKEN_LINK - STR_GLOBAL_CONTEXT_FIRST] + sEntry; Point aEntryPos = GetEntryPosition( pEntry ); aEntryPos.X() = GetTabPos( pEntry, pTab ); Size aSize( pItem->GetSize( this, pEntry ) ); if((aEntryPos.X() + aSize.Width()) > GetSizePixel().Width()) aSize.Width() = GetSizePixel().Width() - aEntryPos.X(); aEntryPos = OutputToScreenPixel(aEntryPos); tools::Rectangle aItemRect( aEntryPos, aSize ); if(Help::IsBalloonHelpEnabled()) { aEntryPos.X() += aSize.Width(); Help::ShowBalloon( this, aEntryPos, aItemRect, sEntry ); } else Help::ShowQuickHelp( this, aItemRect, sEntry, QuickHelpFlags::Left|QuickHelpFlags::VCenter ); } } } if(bParent) SvTreeListBox::RequestHelp(rHEvt); } void SwGlobalTree::SelectHdl() { sal_uLong nSelCount = GetSelectionCount(); SvTreeListEntry* pSel = FirstSelected(); sal_uLong nAbsPos = pSel ? GetModel()->GetAbsPos(pSel) : 0; SwNavigationPI* pNavi = GetParentWindow(); bool bReadonly = !m_pActiveShell || m_pActiveShell->GetView().GetDocShell()->IsReadOnly(); pNavi->m_aGlobalToolBox->EnableItem(pNavi->m_aGlobalToolBox->GetItemId("edit"), nSelCount == 1 && !bReadonly); pNavi->m_aGlobalToolBox->EnableItem(pNavi->m_aGlobalToolBox->GetItemId("insert"), nSelCount <= 1 && !bReadonly); pNavi->m_aGlobalToolBox->EnableItem(pNavi->m_aGlobalToolBox->GetItemId("update"), GetEntryCount() > 0 && !bReadonly); pNavi->m_aGlobalToolBox->EnableItem(pNavi->m_aGlobalToolBox->GetItemId("up"), nSelCount == 1 && nAbsPos && !bReadonly); pNavi->m_aGlobalToolBox->EnableItem(pNavi->m_aGlobalToolBox->GetItemId("down"), nSelCount == 1 && nAbsPos < GetEntryCount() - 1 && !bReadonly); } void SwGlobalTree::DeselectHdl() { SelectHdl(); } DragDropMode SwGlobalTree::NotifyStartDrag( TransferDataContainer& , SvTreeListEntry* pEntry ) { m_bIsInternalDrag = true; m_pDDSource = pEntry; return DragDropMode::CTRL_MOVE; } sal_IntPtr SwGlobalTree::GetTabPos( SvTreeListEntry*, SvLBoxTab* pTab) { return pTab->GetPos() - GLBL_TABPOS_SUB; } TriState SwGlobalTree::NotifyMoving( SvTreeListEntry* pTarget, SvTreeListEntry* pSource, SvTreeListEntry*&, sal_uLong& ) { SvTreeList* _pModel = GetModel(); sal_uLong nSource = _pModel->GetAbsPos(pSource); sal_uLong nDest = pTarget ? _pModel->GetAbsPos(pTarget) : m_pSwGlblDocContents->size(); if( m_pActiveShell->MoveGlobalDocContent( *m_pSwGlblDocContents, nSource, nSource + 1, nDest ) && Update( false )) Display(); return TRISTATE_FALSE; } TriState SwGlobalTree::NotifyCopying( SvTreeListEntry* /*pTarget*/, SvTreeListEntry* /*pEntry*/, SvTreeListEntry*& /*rpNewParent*/, sal_uLong& /*rNewChildPos*/ ) { return TRISTATE_FALSE; } bool SwGlobalTree::NotifyAcceptDrop( SvTreeListEntry* pEntry) { return pEntry != nullptr; } void SwGlobalTree::StartDrag( sal_Int8 nAction, const Point& rPt ) { if( 1 == GetSelectionCount() ) SvTreeListBox::StartDrag( nAction, rPt ); } void SwGlobalTree::DragFinished( sal_Int8 nAction ) { SvTreeListBox::DragFinished( nAction ); m_bIsInternalDrag = false; } // If a Ctrl+DoubleClick is executed in an empty area, // then the base function of the control should be called. void SwGlobalTree::MouseButtonDown( const MouseEvent& rMEvt ) { Point aPos( rMEvt.GetPosPixel()); SvTreeListEntry* pEntry = GetEntry( aPos, true ); if( !pEntry && rMEvt.IsLeft() && rMEvt.IsMod1() && (rMEvt.GetClicks() % 2) == 0) Control::MouseButtonDown( rMEvt ); else SvTreeListBox::MouseButtonDown( rMEvt ); } void SwGlobalTree::GetFocus() { if(Update( false )) Display(); SvTreeListBox::GetFocus(); } void SwGlobalTree::KeyInput(const KeyEvent& rKEvt) { const vcl::KeyCode aCode = rKEvt.GetKeyCode(); if(aCode.GetCode() == KEY_RETURN) { switch(aCode.GetModifier()) { case KEY_MOD2: // Switch boxes GetParentWindow()->ToggleTree(); break; } } else SvTreeListBox::KeyInput(rKEvt); } void SwGlobalTree::Clear() { m_pEmphasisEntry = nullptr; SvTreeListBox::Clear(); } void SwGlobalTree::Display(bool bOnlyUpdateUserData) { size_t nCount = m_pSwGlblDocContents->size(); if(bOnlyUpdateUserData && GetEntryCount() == m_pSwGlblDocContents->size()) { SvTreeListEntry* pEntry = First(); for (size_t i = 0; i < nCount && pEntry; i++) { SwGlblDocContent* pCont = (*m_pSwGlblDocContents)[i]; pEntry->SetUserData(pCont); pEntry = Next(pEntry); assert(pEntry || i == nCount - 1); } } else { SetUpdateMode( false ); SvTreeListEntry* pOldSelEntry = FirstSelected(); OUString sEntryName; // Name of the entry sal_uLong nSelPos = TREELIST_ENTRY_NOTFOUND; if(pOldSelEntry) { sEntryName = GetEntryText(pOldSelEntry); nSelPos = GetModel()->GetAbsPos(pOldSelEntry); } Clear(); if(!m_pSwGlblDocContents) Update( false ); SvTreeListEntry* pSelEntry = nullptr; for( size_t i = 0; i < nCount; i++) { SwGlblDocContent* pCont = (*m_pSwGlblDocContents)[i]; OUString sEntry; Image aImage; switch( pCont->GetType() ) { case GLBLDOC_UNKNOWN: { sEntry = m_aContextStrings[STR_INSERT_TEXT - STR_GLOBAL_CONTEXT_FIRST]; } break; case GLBLDOC_TOXBASE: { const SwTOXBase* pBase = pCont->GetTOX(); sEntry = pBase->GetTitle(); aImage = Image(BitmapEx(RID_BMP_NAVI_INDEX)); } break; case GLBLDOC_SECTION: { const SwSection* pSect = pCont->GetSection(); sEntry = pSect->GetSectionName(); aImage = Image(BitmapEx(RID_BMP_DROP_REGION)); } break; } SvTreeListEntry* pEntry = InsertEntry(sEntry, aImage, aImage, nullptr, false, TREELIST_APPEND, pCont); if(sEntry == sEntryName) { pSelEntry = pEntry; } } if(pSelEntry) { Select(pSelEntry); } else if(nSelPos != TREELIST_ENTRY_NOTFOUND && nSelPos < nCount) { Select(GetEntry(nSelPos)); } else if(nCount) Select(First()); else SelectHdl(); SetUpdateMode( true ); } } void SwGlobalTree::InsertRegion( const SwGlblDocContent* pCont, const OUString* pFileName ) { Sequence< OUString > aFileNames; if ( !pFileName ) { delete m_pDocInserter; m_pDocInserter = new ::sfx2::DocumentInserter( "swriter", sfx2::DocumentInserter::Mode::InsertMulti ); m_pDocInserter->StartExecuteModal( LINK( this, SwGlobalTree, DialogClosedHdl ) ); } else if ( !pFileName->isEmpty() ) { aFileNames.realloc(1); INetURLObject aFileName; aFileName.SetSmartURL( *pFileName ); aFileNames.getArray()[0] = aFileName.GetMainURL( INetURLObject::DecodeMechanism::NONE ); InsertRegion( pCont, aFileNames ); } } void SwGlobalTree::EditContent(const SwGlblDocContent* pCont ) { sal_uInt16 nSlot = 0; switch( pCont->GetType() ) { case GLBLDOC_UNKNOWN: m_pActiveShell->GetView().GetEditWin().GrabFocus(); break; case GLBLDOC_TOXBASE: { const SwTOXBase* pBase = pCont->GetTOX(); if(pBase) nSlot = FN_INSERT_MULTI_TOX; } break; case GLBLDOC_SECTION: { OpenDoc(pCont); nSlot = 0; pCont = nullptr; } break; } if(pCont) GotoContent(pCont); if(nSlot) { m_pActiveShell->GetView().GetViewFrame()->GetDispatcher()->Execute(nSlot); if(Update( false )) Display(); } } IMPL_LINK( SwGlobalTree, PopupHdl, Menu* , pMenu, bool) { ExecuteContextMenuAction( pMenu->GetCurItemId()); return true; } void SwGlobalTree::ExecuteContextMenuAction( sal_uInt16 nSelectedPopupEntry ) { SvTreeListEntry* pEntry = FirstSelected(); SwGlblDocContent* pCont = pEntry ? static_cast(pEntry->GetUserData()) : nullptr; // If a RequestHelp is called during the dialogue, // then the content gets lost. Because of that a copy // is created in which only the DocPos is set correctly. SwGlblDocContent* pContCopy = nullptr; if(pCont) pContCopy = new SwGlblDocContent(pCont->GetDocPos()); SfxDispatcher& rDispatch = *m_pActiveShell->GetView().GetViewFrame()->GetDispatcher(); sal_uInt16 nSlot = 0; bool bDeleteContentCopy = true; switch( nSelectedPopupEntry ) { case CTX_UPDATE_SEL: { // Two passes: first update the areas, then the directories. SvTreeListEntry* pSelEntry = FirstSelected(); while( pSelEntry ) { SwGlblDocContent* pContent = static_cast(pSelEntry->GetUserData()); if(GLBLDOC_SECTION == pContent->GetType() && pContent->GetSection()->IsConnected()) { const_cast(pContent->GetSection())->UpdateNow(); } pSelEntry = NextSelected(pSelEntry); } pSelEntry = FirstSelected(); while( pSelEntry ) { SwGlblDocContent* pContent = static_cast(pSelEntry->GetUserData()); if(GLBLDOC_TOXBASE == pContent->GetType()) m_pActiveShell->UpdateTableOf(*pContent->GetTOX()); pSelEntry = NextSelected(pSelEntry); } } break; case CTX_UPDATE_INDEX: { nSlot = FN_UPDATE_TOX; } break; case CTX_UPDATE_LINK: case CTX_UPDATE_ALL: { m_pActiveShell->GetLinkManager().UpdateAllLinks(true, false, nullptr); if(CTX_UPDATE_ALL == nSelectedPopupEntry) nSlot = FN_UPDATE_TOX; pCont = nullptr; } break; case CTX_EDIT: { OSL_ENSURE(pCont, "edit without entry ? " ); if (pCont) { EditContent(pCont); } } break; case CTX_EDIT_LINK: { OSL_ENSURE(pCont, "edit without entry ? " ); if (pCont) { SfxStringItem aName(FN_EDIT_REGION, pCont->GetSection()->GetSectionName()); rDispatch.ExecuteList(FN_EDIT_REGION, SfxCallMode::ASYNCHRON, { &aName }); } } break; case CTX_DELETE: { // If several entries selected, then after each delete the array // must be refilled. So you do not have to remember anything, // deleting begins at the end. SvTreeListEntry* pSelEntry = LastSelected(); std::unique_ptr pTempContents; m_pActiveShell->StartAction(); while(pSelEntry) { m_pActiveShell->DeleteGlobalDocContent( pTempContents ? *pTempContents : *m_pSwGlblDocContents, GetModel()->GetAbsPos(pSelEntry)); pSelEntry = PrevSelected(pSelEntry); if(pSelEntry) { pTempContents.reset(new SwGlblDocContents); m_pActiveShell->GetGlobalDocContent(*pTempContents); } } pTempContents.reset(); m_pActiveShell->EndAction(); pCont = nullptr; } break; case CTX_INSERT_ANY_INDEX: { if(pContCopy) { SfxItemSet aSet(m_pActiveShell->GetView().GetPool(), RES_COL, RES_COL, RES_BACKGROUND, RES_BACKGROUND, RES_FRM_SIZE, RES_FRM_SIZE, SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE, RES_LR_SPACE, RES_LR_SPACE, FN_PARAM_TOX_TYPE, FN_PARAM_TOX_TYPE, 0); SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create(); assert(pFact && "Dialog creation failed!"); ScopedVclPtr pDlg(pFact->CreateMultiTOXTabDialog( this, aSet, *m_pActiveShell, nullptr, true)); assert(pDlg && "Dialog creation failed!"); if(RET_OK == pDlg->Execute()) { SwTOXDescription& rDesc = pDlg->GetTOXDescription( pDlg->GetCurrentTOXType()); SwTOXMgr aMgr(m_pActiveShell); SwTOXBase* pToInsert = nullptr; if(aMgr.UpdateOrInsertTOX(rDesc, &pToInsert, pDlg->GetOutputItemSet())) m_pActiveShell->InsertGlobalDocContent( *pContCopy, *pToInsert ); } pCont = nullptr; } } break; case CTX_INSERT_FILE: { bDeleteContentCopy = false; m_pDocContent = pContCopy; InsertRegion( pContCopy ); pCont = nullptr; } break; case CTX_INSERT_NEW_FILE: { SfxViewFrame* pGlobFrame = m_pActiveShell->GetView().GetViewFrame(); SwGlobalFrameListener_Impl aFrameListener(*pGlobFrame); sal_uLong nEntryPos = pEntry ? GetModel()->GetAbsPos(pEntry) : (sal_uLong)-1; // Creating a new doc SfxStringItem aFactory(SID_NEWDOCDIRECT, SwDocShell::Factory().GetFilterContainer()->GetName()); const SfxFrameItem* pItem = static_cast( rDispatch.ExecuteList(SID_NEWDOCDIRECT, SfxCallMode::SYNCHRON, { &aFactory })); // save at SfxFrame* pFrame = pItem ? pItem->GetFrame() : nullptr; SfxViewFrame* pViewFrame = pFrame ? pFrame->GetCurrentViewFrame() : nullptr; if (pViewFrame) { const SfxBoolItem* pBool = static_cast( pViewFrame->GetDispatcher()->Execute( SID_SAVEASDOC, SfxCallMode::SYNCHRON )); SfxObjectShell& rObj = *pViewFrame->GetObjectShell(); const SfxMedium* pMedium = rObj.GetMedium(); OUString sNewFile(pMedium->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri)); // Insert the area with the Doc-Name // Bring the own Doc in the foreground if(aFrameListener.IsValid() && !sNewFile.isEmpty()) { pGlobFrame->ToTop(); // Due to the update the entries are invalid if(nEntryPos != (sal_uLong)-1) { Update( false ); Display(); Select(GetModel()->GetEntryAtAbsPos(nEntryPos)); pEntry = FirstSelected(); pCont = pEntry ? static_cast(pEntry->GetUserData()) : nullptr; } else { pEntry = nullptr; pCont = nullptr; } if(pBool->GetValue()) { InsertRegion(pCont, &sNewFile); pViewFrame->ToTop(); } else pViewFrame->GetDispatcher()->Execute(SID_CLOSEWIN, SfxCallMode::SYNCHRON); } else { pViewFrame->ToTop(); return; } } } break; case CTX_INSERT_TEXT: { if(pCont) m_pActiveShell->InsertGlobalDocContent(*pCont); else { m_pActiveShell->SplitNode(); // Empty document m_pActiveShell->Up( false ); } m_pActiveShell->GetView().GetEditWin().GrabFocus(); } break; case CTX_UPDATE: pCont = nullptr; break; default:; // here nothing happens } if(pCont) GotoContent(pCont); if(nSlot) rDispatch.Execute(nSlot); if(Update( false )) Display(); if ( bDeleteContentCopy ) delete pContCopy; } IMPL_LINK_NOARG(SwGlobalTree, Timeout, Timer *, void) { if(!IsDisposed() && !HasFocus() && Update( false )) Display(); } void SwGlobalTree::GotoContent(const SwGlblDocContent* pCont) { m_pActiveShell->EnterStdMode(); switch( pCont->GetType() ) { case GLBLDOC_UNKNOWN: m_pActiveShell->GotoGlobalDocContent(*pCont); break; case GLBLDOC_TOXBASE: { const OUString sName = pCont->GetTOX()->GetTOXName(); if (!m_pActiveShell->GotoNextTOXBase(&sName)) m_pActiveShell->GotoPrevTOXBase(&sName); } break; case GLBLDOC_SECTION: break; } } void SwGlobalTree::ShowTree() { m_aUpdateTimer.Start(); SvTreeListBox::Show(); } void SwGlobalTree::HideTree() { m_aUpdateTimer.Stop(); SvTreeListBox::Hide(); } void SwGlobalTree::ExecCommand(const OUString &rCmd) { SvTreeListEntry* pEntry = FirstSelected(); OSL_ENSURE(pEntry, "It explodes in the next moment"); if (rCmd == "edit") { const SwGlblDocContent* pCont = static_cast( pEntry->GetUserData()); EditContent(pCont); } else { if(GetSelectionCount() == 1) { bool bMove = false; sal_uLong nSource = GetModel()->GetAbsPos(pEntry); sal_uLong nDest = nSource; if (rCmd == "down") { sal_uLong nEntryCount = GetEntryCount(); bMove = nEntryCount > nSource + 1; nDest+= 2; } else if (rCmd == "up") { if(nSource) bMove = 0 != nSource; nDest--; } if( bMove && m_pActiveShell->MoveGlobalDocContent( *m_pSwGlblDocContents, nSource, nSource + 1, nDest ) && Update( false )) Display(); } } } bool SwGlobalTree::Update(bool bHard) { SwView* pActView = GetParentWindow()->GetCreateView(); bool bRet = false; if(pActView && pActView->GetWrtShellPtr()) { const SwWrtShell* pOldShell = m_pActiveShell; m_pActiveShell = pActView->GetWrtShellPtr(); if(m_pActiveShell != pOldShell) { delete m_pSwGlblDocContents; m_pSwGlblDocContents = nullptr; } if(!m_pSwGlblDocContents) { m_pSwGlblDocContents = new SwGlblDocContents; bRet = true; m_pActiveShell->GetGlobalDocContent(*m_pSwGlblDocContents); } else { bool bCopy = false; std::unique_ptr pTempContents(new SwGlblDocContents); m_pActiveShell->GetGlobalDocContent(*pTempContents); if(pTempContents->size() != m_pSwGlblDocContents->size() || pTempContents->size() != GetEntryCount()) { bRet = true; bCopy = true; } else { for(size_t i = 0; i < pTempContents->size() && !bCopy; i++) { SwGlblDocContent* pLeft = (*pTempContents)[i]; SwGlblDocContent* pRight = (*m_pSwGlblDocContents)[i]; GlobalDocContentType eType = pLeft->GetType(); SvTreeListEntry* pEntry = GetEntry(i); OUString sTemp = GetEntryText(pEntry); if ( eType != pRight->GetType() || ( eType == GLBLDOC_SECTION && pLeft->GetSection()->GetSectionName() != sTemp ) || ( eType == GLBLDOC_TOXBASE && pLeft->GetTOX()->GetTitle() != sTemp ) ) { bCopy = bRet = true; } } } if(bCopy || bHard) { m_pSwGlblDocContents->DeleteAndDestroyAll(); m_pSwGlblDocContents->insert( *pTempContents ); pTempContents->clear(); } } } else { Clear(); if(m_pSwGlblDocContents) m_pSwGlblDocContents->DeleteAndDestroyAll(); } // FIXME: Implement a test for changes! return bRet; } void SwGlobalTree::OpenDoc(const SwGlblDocContent* pCont) { const OUString sFileName(pCont->GetSection()->GetLinkFileName().getToken(0, sfx2::cTokenSeparator)); bool bFound = false; const SfxObjectShell* pCurr = SfxObjectShell::GetFirst(); while( !bFound && pCurr ) { if(pCurr->GetMedium() && pCurr->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri) == sFileName) { bFound = true; SwGlobalTree::SetShowShell(pCurr); Application::PostUserEvent( LINK( this, SwGlobalTree, ShowFrameHdl ), nullptr, true ); pCurr = nullptr; } else pCurr = SfxObjectShell::GetNext(*pCurr); } if(!bFound) { SfxStringItem aURL(SID_FILE_NAME, sFileName); SfxBoolItem aReadOnly(SID_DOC_READONLY, false); SfxStringItem aTargetFrameName( SID_TARGETNAME, "_blank" ); SfxStringItem aReferer(SID_REFERER, m_pActiveShell->GetView().GetDocShell()->GetTitle()); m_pActiveShell->GetView().GetViewFrame()->GetDispatcher()-> ExecuteList(SID_OPENDOC, SfxCallMode::ASYNCHRON, { &aURL, &aReadOnly, &aReferer, &aTargetFrameName }); } } IMPL_LINK_NOARG( SwGlobalTree, DoubleClickHdl, SvTreeListBox*, bool) { SvTreeListEntry* pEntry = GetCurEntry(); SwGlblDocContent* pCont = static_cast(pEntry->GetUserData()); if(pCont->GetType() == GLBLDOC_SECTION) OpenDoc(pCont); else { GotoContent(pCont); m_pActiveShell->GetView().GetEditWin().GrabFocus(); } return false; } SwNavigationPI* SwGlobalTree::GetParentWindow() { return m_xDialog; } IMPL_STATIC_LINK_NOARG(SwGlobalTree, ShowFrameHdl, void*, void) { SfxViewFrame* pFirst = pShowShell ? SfxViewFrame::GetFirst(pShowShell) : nullptr; if (pFirst) pFirst->ToTop(); SwGlobalTree::SetShowShell(nullptr); } void SwGlobalTree::InitEntry(SvTreeListEntry* pEntry, const OUString& rStr ,const Image& rImg1,const Image& rImg2, SvLBoxButtonKind eButtonKind) { const size_t nColToHilite = 1; //0==Bitmap;1=="Column1";2=="Column2" SvTreeListBox::InitEntry( pEntry, rStr, rImg1, rImg2, eButtonKind ); SvLBoxString& rCol = static_cast(pEntry->GetItem( nColToHilite )); pEntry->ReplaceItem(o3tl::make_unique(rCol.GetText()), nColToHilite); } void SwLBoxString::Paint(const Point& rPos, SvTreeListBox& rDev, vcl::RenderContext& rRenderContext, const SvViewDataEntry* pView, const SvTreeListEntry& rEntry) { SwGlblDocContent* pCont = static_cast(rEntry.GetUserData()); if (pCont->GetType() == GLBLDOC_SECTION && !(pCont->GetSection())->IsConnectFlag()) { rRenderContext.Push(PushFlags::FONT); vcl::Font aOldFont(rRenderContext.GetFont()); vcl::Font aFont(rRenderContext.GetFont()); Color aCol(COL_LIGHTRED); aFont.SetColor(aCol); rRenderContext.SetFont(aFont); rRenderContext.DrawText(rPos, GetText()); rRenderContext.Pop(); } else SvLBoxString::Paint(rPos, rDev, rRenderContext, pView, rEntry); } void SwGlobalTree::DataChanged( const DataChangedEvent& rDCEvt ) { if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) { Update(true); } SvTreeListBox::DataChanged( rDCEvt ); } void SwGlobalTree::InsertRegion( const SwGlblDocContent* _pContent, const Sequence< OUString >& _rFiles ) { sal_Int32 nFiles = _rFiles.getLength(); if ( nFiles ) { bool bMove = false; if ( !_pContent ) { SvTreeListEntry* pLast = LastVisible(); _pContent = static_cast(pLast->GetUserData()); bMove = true; } OUString sFilePassword; sal_uLong nEntryCount = GetEntryCount(); const OUString* pFileNames = _rFiles.getConstArray(); SwWrtShell& rSh = GetParentWindow()->GetCreateView()->GetWrtShell(); rSh.StartAction(); // after insertion of the first new content the 'pCont' parameter becomes invalid // find the index of the 'anchor' content to always use a current anchor content size_t nAnchorContent = m_pSwGlblDocContents->size() - 1; if ( !bMove ) { for (size_t nContent = 0; nContent < m_pSwGlblDocContents->size(); ++nContent) { if( *_pContent == *(*m_pSwGlblDocContents)[ nContent ] ) { nAnchorContent = nContent; break; } } } SwGlblDocContents aTempContents; for ( sal_Int32 nFile = 0; nFile < nFiles; ++nFile ) { //update the global document content after each inserted document rSh.GetGlobalDocContent(aTempContents); SwGlblDocContent* pAnchorContent = nullptr; OSL_ENSURE(aTempContents.size() > (nAnchorContent + nFile), "invalid anchor content -> last insertion failed"); if ( aTempContents.size() > (nAnchorContent + nFile) ) pAnchorContent = aTempContents[nAnchorContent + nFile]; else pAnchorContent = aTempContents.back(); OUString sFileName(pFileNames[nFile]); INetURLObject aFileUrl; aFileUrl.SetSmartURL( sFileName ); OUString sSectionName(aFileUrl.GetLastName( INetURLObject::DecodeMechanism::Unambiguous).getToken(0, sfx2::cTokenSeparator)); sal_uInt16 nSectCount = rSh.GetSectionFormatCount(); OUString sTempSectionName(sSectionName); sal_uInt16 nAddNumber = 0; sal_uInt16 nCount = 0; // if applicable: add index if the range name is already in use. while ( nCount < nSectCount ) { const SwSectionFormat& rFormat = rSh.GetSectionFormat(nCount); if ((rFormat.GetSection()->GetSectionName() == sTempSectionName) && rFormat.IsInNodesArr()) { nCount = 0; nAddNumber++; sTempSectionName = sSectionName + ":" + OUString::number( nAddNumber ); } else nCount++; } if ( nAddNumber ) sSectionName = sTempSectionName; SwSectionData aSectionData(CONTENT_SECTION, sSectionName); aSectionData.SetProtectFlag(true); aSectionData.SetHidden(false); aSectionData.SetLinkFileName(sFileName); aSectionData.SetType(FILE_LINK_SECTION); aSectionData.SetLinkFilePassword( sFilePassword ); rSh.InsertGlobalDocContent( *pAnchorContent, aSectionData ); } if ( bMove ) { Update( false ); rSh.MoveGlobalDocContent( *m_pSwGlblDocContents, nEntryCount, nEntryCount + nFiles, nEntryCount - nFiles ); } rSh.EndAction(); Update( false ); Display(); } } IMPL_LINK( SwGlobalTree, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void ) { if ( ERRCODE_NONE != _pFileDlg->GetError() ) return; std::unique_ptr pMedList(m_pDocInserter->CreateMediumList()); if ( pMedList ) { Sequence< OUString >aFileNames( pMedList->size() ); OUString* pFileNames = aFileNames.getArray(); sal_Int32 nPos = 0; for (SfxMedium* pMed : *pMedList) { OUString sFileName = pMed->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) + OUStringLiteral1(sfx2::cTokenSeparator) + pMed->GetFilter()->GetFilterName() + OUStringLiteral1(sfx2::cTokenSeparator); pFileNames[nPos++] = sFileName; } pMedList.reset(); InsertRegion( m_pDocContent, aFileNames ); DELETEZ( m_pDocContent ); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */