/* -*- 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 // UNO-Stuff #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; // Userdata-struct for tree-entries struct TargetData { OUString aUStrLinkname; bool bIsTarget; TargetData (const OUString& aUStrLName, bool bTarget) : bIsTarget(bTarget) { if (bIsTarget) aUStrLinkname = aUStrLName; } }; // Tree-Window SvxHlmarkTreeLBox::SvxHlmarkTreeLBox(vcl::Window* pParent, WinBits nStyle) : SvTreeListBox(pParent, nStyle) , mpParentWnd(nullptr) { SetNodeDefaultImages(); } SvxHlmarkTreeLBox::~SvxHlmarkTreeLBox() { disposeOnce(); } void SvxHlmarkTreeLBox::dispose() { mpParentWnd.clear(); SvTreeListBox::dispose(); } VCL_BUILDER_FACTORY_CONSTRUCTOR(SvxHlmarkTreeLBox, WB_TABSTOP) Size SvxHlmarkTreeLBox::GetOptimalSize() const { return LogicToPixel(Size(103, 162), MapMode(MapUnit::MapAppFont)); } void SvxHlmarkTreeLBox::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) { if (!mpParentWnd || mpParentWnd->mnError == LERR_NOERROR) { SvTreeListBox::Paint(rRenderContext, rRect); } else { Erase(rRenderContext); ::tools::Rectangle aDrawRect(Point( 0, 0 ), GetSizePixel()); OUString aStrMessage; switch (mpParentWnd->mnError) { case LERR_NOENTRIES : aStrMessage = CuiResId( RID_SVXSTR_HYPDLG_ERR_LERR_NOENTRIES ); break; case LERR_DOCNOTOPEN : aStrMessage = CuiResId( RID_SVXSTR_HYPDLG_ERR_LERR_DOCNOTOPEN ); break; } rRenderContext.DrawText(aDrawRect, aStrMessage, DrawTextFlags::Left | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak); } } //*** Window-Class *** // Constructor / Destructor SvxHlinkDlgMarkWnd::SvxHlinkDlgMarkWnd( SvxHyperlinkTabPageBase *pParent ) : FloatingWindow(pParent, "HyperlinkMark", "cui/ui/hyperlinkmarkdialog.ui") , mbUserMoved(false) , mpParent(pParent) , mnError(LERR_NOERROR) { get(mpBtApply, "apply"); get(mpBtClose, "close"); get(mpLbTree, "TreeListBox"); mpLbTree->SetParentWnd(this); mpBtApply->SetClickHdl ( LINK ( this, SvxHlinkDlgMarkWnd, ClickApplyHdl_Impl ) ); mpBtClose->SetClickHdl ( LINK ( this, SvxHlinkDlgMarkWnd, ClickCloseHdl_Impl ) ); mpLbTree->SetDoubleClickHdl ( LINK ( this, SvxHlinkDlgMarkWnd, DoubleClickApplyHdl_Impl ) ); // add lines to the Tree-ListBox mpLbTree->SetStyle( mpLbTree->GetStyle() | WB_TABSTOP | WB_BORDER | WB_HASLINES | WB_HASBUTTONS | //WB_HASLINESATROOT | WB_HSCROLL | WB_HASBUTTONSATROOT ); } SvxHlinkDlgMarkWnd::~SvxHlinkDlgMarkWnd() { disposeOnce(); } void SvxHlinkDlgMarkWnd::dispose() { ClearTree(); mpBtApply.clear(); mpBtClose.clear(); mpLbTree.clear(); mpParent.clear(); FloatingWindow::dispose(); } // Set an errorstatus sal_uInt16 SvxHlinkDlgMarkWnd::SetError( sal_uInt16 nError) { sal_uInt16 nOldError = mnError; mnError = nError; if( mnError != LERR_NOERROR ) ClearTree(); mpLbTree->Invalidate(); return nOldError; } // Move window bool SvxHlinkDlgMarkWnd::MoveTo ( Point aNewPos ) { if ( !mbUserMoved ) { bool bOldStatus = mbUserMoved; SetPosPixel ( aNewPos ); mbUserMoved = bOldStatus; } return mbUserMoved; } void SvxHlinkDlgMarkWnd::Move () { Window::Move(); if ( IsReallyVisible() ) mbUserMoved = true; } bool SvxHlinkDlgMarkWnd::ConnectToDialog() { bool bOldStatus = mbUserMoved; mbUserMoved = true; return bOldStatus; } namespace { void SelectPath(SvTreeListEntry *pEntry, SvxHlmarkTreeLBox &rLbTree, std::deque &rLastSelectedPath) { OUString sTitle(rLastSelectedPath.front()); rLastSelectedPath.pop_front(); if (sTitle.isEmpty()) return; while (pEntry) { if (sTitle == rLbTree.GetEntryText(pEntry)) { rLbTree.Select(pEntry); rLbTree.MakeVisible(pEntry); if (!rLastSelectedPath.empty()) { rLbTree.Expand(pEntry); SelectPath(rLbTree.FirstChild(pEntry), rLbTree, rLastSelectedPath); } break; } pEntry = pEntry->NextSibling(); } } } #define TG_SETTING_MANAGER "TargetInDocument" #define TG_SETTING_LASTMARK "LastSelectedMark" #define TG_SETTING_LASTPATH "LastSelectedPath" void SvxHlinkDlgMarkWnd::RestoreLastSelection() { bool bSelectedEntry = false; OUString sLastSelectedMark; std::deque aLastSelectedPath; SvtViewOptions aViewSettings( EViewType::Dialog, TG_SETTING_MANAGER ); if (aViewSettings.Exists()) { //Maybe we might want to have some sort of mru list and keep a mapping //per document, rather than the current reuse of "the last thing //selected, regardless of the document" aViewSettings.GetUserItem(TG_SETTING_LASTMARK) >>= sLastSelectedMark; uno::Sequence aTmp; aViewSettings.GetUserItem(TG_SETTING_LASTPATH) >>= aTmp; aLastSelectedPath = comphelper::sequenceToContainer< std::deque >(aTmp); } //fallback to previous entry selected the last time we executed this dialog. //First see if the exact mark exists and re-use that if (!sLastSelectedMark.isEmpty()) bSelectedEntry = SelectEntry(sLastSelectedMark); //Otherwise just select the closest path available //now to what was available at dialog close time if (!bSelectedEntry && !aLastSelectedPath.empty()) { std::deque aTmpSelectedPath(aLastSelectedPath); SelectPath(mpLbTree->First(), *mpLbTree, aTmpSelectedPath); } } // Interface to refresh tree void SvxHlinkDlgMarkWnd::RefreshTree (const OUString& aStrURL) { OUString aUStrURL; EnterWait(); ClearTree(); sal_Int32 nPos = aStrURL.indexOf('#'); if (nPos != 0) aUStrURL = aStrURL; if (!RefreshFromDoc(aUStrURL)) mpLbTree->Invalidate(); bool bSelectedEntry = false; if ( nPos != -1 ) { OUString aStrMark = aStrURL.copy(nPos+1); bSelectedEntry = SelectEntry(aStrMark); } if (!bSelectedEntry) RestoreLastSelection(); LeaveWait(); } // get links from document bool SvxHlinkDlgMarkWnd::RefreshFromDoc(const OUString& aURL) { mnError = LERR_NOERROR; uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( ::comphelper::getProcessComponentContext() ); uno::Reference< lang::XComponent > xComp; if( !aURL.isEmpty() ) { // load from url uno::Reference< frame::XComponentLoader > xLoader( xDesktop, uno::UNO_QUERY ); if( xLoader.is() ) { try { uno::Sequence< beans::PropertyValue > aArg(1); aArg.getArray()[0].Name = "Hidden"; aArg.getArray()[0].Value <<= true; xComp = xLoader->loadComponentFromURL( aURL, "_blank", 0, aArg ); } catch( const io::IOException& ) { } catch( const lang::IllegalArgumentException& ) { } } } else { // the component with user focus ( current document ) xComp = xDesktop->getCurrentComponent(); } if( xComp.is() ) { uno::Reference< document::XLinkTargetSupplier > xLTS( xComp, uno::UNO_QUERY ); if( xLTS.is() ) { if( FillTree( xLTS->getLinks() ) == 0 ) mnError = LERR_NOENTRIES; } else mnError = LERR_DOCNOTOPEN; if ( !aURL.isEmpty() ) xComp->dispose(); } else { if( !aURL.isEmpty() ) mnError=LERR_DOCNOTOPEN; } return (mnError==0); } // Fill Tree-Control int SvxHlinkDlgMarkWnd::FillTree( const uno::Reference< container::XNameAccess >& xLinks, SvTreeListEntry* pParentEntry ) { int nEntries=0; const uno::Sequence< OUString > aNames( xLinks->getElementNames() ); const sal_uLong nLinks = aNames.getLength(); const OUString* pNames = aNames.getConstArray(); const OUString aProp_LinkDisplayName( "LinkDisplayName" ); const OUString aProp_LinkTarget( "com.sun.star.document.LinkTarget" ); const OUString aProp_LinkDisplayBitmap( "LinkDisplayBitmap" ); for( sal_uLong i = 0; i < nLinks; i++ ) { uno::Any aAny; OUString aLink( *pNames++ ); bool bError = false; try { aAny = xLinks->getByName( aLink ); } catch(const uno::Exception&) { // if the name of the target was invalid (like empty headings) // no object can be provided bError = true; } if(bError) continue; uno::Reference< beans::XPropertySet > xTarget; if( aAny >>= xTarget ) { try { // get name to display aAny = xTarget->getPropertyValue( aProp_LinkDisplayName ); OUString aDisplayName; aAny >>= aDisplayName; OUString aStrDisplayname ( aDisplayName ); // is it a target ? uno::Reference< lang::XServiceInfo > xSI( xTarget, uno::UNO_QUERY ); bool bIsTarget = xSI->supportsService( aProp_LinkTarget ); // create userdata TargetData *pData = new TargetData ( aLink, bIsTarget ); SvTreeListEntry* pEntry; try { // get bitmap for the tree-entry uno::Reference< awt::XBitmap > aXBitmap( xTarget->getPropertyValue( aProp_LinkDisplayBitmap ), uno::UNO_QUERY ); if( aXBitmap.is() ) { Image aBmp(BitmapEx(VCLUnoHelper::GetBitmap(aXBitmap).GetBitmap(), /*mask*/COL_LIGHTMAGENTA)); // insert Displayname into treelist with bitmaps pEntry = mpLbTree->InsertEntry ( aStrDisplayname, aBmp, aBmp, pParentEntry, false, TREELIST_APPEND, static_cast(pData) ); nEntries++; } else { // insert Displayname into treelist without bitmaps pEntry = mpLbTree->InsertEntry ( aStrDisplayname, pParentEntry, false, TREELIST_APPEND, static_cast(pData) ); nEntries++; } } catch(const css::uno::Exception&) { // insert Displayname into treelist without bitmaps pEntry = mpLbTree->InsertEntry ( aStrDisplayname, pParentEntry, false, TREELIST_APPEND, static_cast(pData) ); nEntries++; } uno::Reference< document::XLinkTargetSupplier > xLTS( xTarget, uno::UNO_QUERY ); if( xLTS.is() ) nEntries += FillTree( xLTS->getLinks(), pEntry ); } catch(const css::uno::Exception&) { } } } return nEntries; } // Clear Tree void SvxHlinkDlgMarkWnd::ClearTree() { SvTreeListEntry* pEntry = mpLbTree->First(); while ( pEntry ) { TargetData* pUserData = static_cast(pEntry->GetUserData()); delete pUserData; pEntry = mpLbTree->Next( pEntry ); } mpLbTree->Clear(); } // Find Entry for String SvTreeListEntry* SvxHlinkDlgMarkWnd::FindEntry (const OUString& aStrName) { bool bFound=false; SvTreeListEntry* pEntry = mpLbTree->First(); while ( pEntry && !bFound ) { TargetData* pUserData = static_cast(pEntry->GetUserData ()); if (aStrName == pUserData->aUStrLinkname) bFound = true; else pEntry = mpLbTree->Next( pEntry ); } return pEntry; } // Select Entry bool SvxHlinkDlgMarkWnd::SelectEntry(const OUString& aStrMark) { SvTreeListEntry* pEntry = FindEntry(aStrMark); if (!pEntry) return false; mpLbTree->Select(pEntry); mpLbTree->MakeVisible (pEntry); return true; } // Click on Apply-Button / Double-click on item in tree IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, DoubleClickApplyHdl_Impl, SvTreeListBox*, bool) { ClickApplyHdl_Impl(nullptr); return false; } IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, ClickApplyHdl_Impl, Button*, void) { SvTreeListEntry* pEntry = mpLbTree->GetCurEntry(); if ( pEntry ) { TargetData *pData = static_cast(pEntry->GetUserData()); if ( pData->bIsTarget ) { mpParent->SetMarkStr(pData->aUStrLinkname); } } } // Click on Close-Button IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, ClickCloseHdl_Impl, Button*, void) { SvTreeListEntry* pEntry = mpLbTree->GetCurEntry(); if ( pEntry ) { TargetData* pUserData = static_cast(pEntry->GetUserData()); OUString sLastSelectedMark = pUserData->aUStrLinkname; std::deque aLastSelectedPath; if (pEntry) { //If the bottommost entry is expanded but nothing //underneath it is selected leave a dummy entry if (mpLbTree->IsExpanded(pEntry)) aLastSelectedPath.push_front(OUString()); while (pEntry) { aLastSelectedPath.push_front(mpLbTree->GetEntryText(pEntry)); pEntry = mpLbTree->GetParent(pEntry); } } uno::Sequence< beans::NamedValue > aSettings { { TG_SETTING_LASTMARK, css::uno::Any(sLastSelectedMark) }, { TG_SETTING_LASTPATH, css::uno::Any(comphelper::containerToSequence(aLastSelectedPath)) } }; // write SvtViewOptions aViewSettings( EViewType::Dialog, TG_SETTING_MANAGER ); aViewSettings.SetUserData( aSettings ); } Close(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */