525 lines
17 KiB
C++
525 lines
17 KiB
C++
/* -*- 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 <dialmgr.hxx>
|
|
#include <o3tl/any.hxx>
|
|
#include <comphelper/propertyvalue.hxx>
|
|
#include <unotools/viewoptions.hxx>
|
|
#include <vcl/graph.hxx>
|
|
|
|
// UNO-Stuff
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <comphelper/sequence.hxx>
|
|
#include <com/sun/star/awt/XBitmap.hpp>
|
|
#include <com/sun/star/frame/Desktop.hpp>
|
|
#include <com/sun/star/beans/NamedValue.hpp>
|
|
#include <com/sun/star/beans/PropertyValue.hpp>
|
|
#include <com/sun/star/document/XLinkTargetSupplier.hpp>
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/io/IOException.hpp>
|
|
|
|
#include <toolkit/helper/vclunohelper.hxx>
|
|
|
|
#include <strings.hrc>
|
|
#include <hlmarkwn.hxx>
|
|
#include <hltpbase.hxx>
|
|
#include <hlmarkwn_def.hxx>
|
|
|
|
#include <stack>
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
namespace {
|
|
|
|
// Userdata-struct for tree-entries
|
|
struct TargetData
|
|
{
|
|
OUString aUStrLinkname;
|
|
bool bIsTarget;
|
|
|
|
TargetData (const OUString& aUStrLName, bool bTarget)
|
|
: bIsTarget(bTarget)
|
|
{
|
|
if (bIsTarget)
|
|
aUStrLinkname = aUStrLName;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
//*** Window-Class ***
|
|
// Constructor / Destructor
|
|
SvxHlinkDlgMarkWnd::SvxHlinkDlgMarkWnd(weld::Window* pParentDialog, SvxHyperlinkTabPageBase *pParentPage)
|
|
: GenericDialogController(pParentDialog, u"cui/ui/hyperlinkmarkdialog.ui"_ustr, u"HyperlinkMark"_ustr)
|
|
, mpParent(pParentPage)
|
|
, mnError(LERR_NOERROR)
|
|
, mxBtApply(m_xBuilder->weld_button(u"ok"_ustr))
|
|
, mxBtClose(m_xBuilder->weld_button(u"close"_ustr))
|
|
, mxLbTree(m_xBuilder->weld_tree_view(u"TreeListBox"_ustr))
|
|
, mxError(m_xBuilder->weld_label(u"error"_ustr))
|
|
{
|
|
mxLbTree->set_size_request(mxLbTree->get_approximate_digit_width() * 25,
|
|
mxLbTree->get_height_rows(12));
|
|
mxBtApply->connect_clicked( LINK ( this, SvxHlinkDlgMarkWnd, ClickApplyHdl_Impl ) );
|
|
mxBtClose->connect_clicked( LINK ( this, SvxHlinkDlgMarkWnd, ClickCloseHdl_Impl ) );
|
|
mxLbTree->connect_row_activated( LINK ( this, SvxHlinkDlgMarkWnd, DoubleClickApplyHdl_Impl ) );
|
|
|
|
// tdf#149935 - remember last used position and size
|
|
SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id());
|
|
if (aDlgOpt.Exists())
|
|
m_xDialog->set_window_state(aDlgOpt.GetWindowState());
|
|
}
|
|
|
|
SvxHlinkDlgMarkWnd::~SvxHlinkDlgMarkWnd()
|
|
{
|
|
ClearTree();
|
|
// tdf#149935 - remember last used position and size
|
|
SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id());
|
|
aDlgOpt.SetWindowState(m_xDialog->get_window_state(vcl::WindowDataMask::PosSize));
|
|
}
|
|
|
|
void SvxHlinkDlgMarkWnd::ErrorChanged()
|
|
{
|
|
if (mnError == LERR_NOENTRIES)
|
|
{
|
|
OUString aStrMessage = CuiResId( RID_CUISTR_HYPDLG_ERR_LERR_NOENTRIES );
|
|
mxError->set_label(aStrMessage);
|
|
mxError->show();
|
|
mxLbTree->hide();
|
|
}
|
|
else if (mnError == LERR_DOCNOTOPEN)
|
|
{
|
|
OUString aStrMessage = CuiResId( RID_CUISTR_HYPDLG_ERR_LERR_DOCNOTOPEN );
|
|
mxError->set_label(aStrMessage);
|
|
mxError->show();
|
|
mxLbTree->hide();
|
|
}
|
|
else
|
|
{
|
|
mxLbTree->show();
|
|
mxError->hide();
|
|
}
|
|
}
|
|
|
|
// Set an errorstatus
|
|
sal_uInt16 SvxHlinkDlgMarkWnd::SetError( sal_uInt16 nError)
|
|
{
|
|
sal_uInt16 nOldError = mnError;
|
|
mnError = nError;
|
|
|
|
if( mnError != LERR_NOERROR )
|
|
ClearTree();
|
|
|
|
ErrorChanged();
|
|
|
|
return nOldError;
|
|
}
|
|
|
|
// Move window
|
|
void SvxHlinkDlgMarkWnd::MoveTo(const Point& rNewPos)
|
|
{
|
|
// tdf#149935 - remember last used position and size
|
|
SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id());
|
|
if (aDlgOpt.Exists())
|
|
m_xDialog->set_window_state(aDlgOpt.GetWindowState());
|
|
else
|
|
m_xDialog->window_move(rNewPos.X(), rNewPos.Y());
|
|
}
|
|
|
|
namespace
|
|
{
|
|
void SelectPath(weld::TreeIter* pEntry, weld::TreeView& rLbTree,
|
|
std::deque<OUString> &rLastSelectedPath)
|
|
{
|
|
OUString sTitle(rLastSelectedPath.front());
|
|
rLastSelectedPath.pop_front();
|
|
if (sTitle.isEmpty())
|
|
return;
|
|
while (pEntry)
|
|
{
|
|
if (sTitle == rLbTree.get_text(*pEntry))
|
|
{
|
|
rLbTree.select(*pEntry);
|
|
rLbTree.scroll_to_row(*pEntry);
|
|
if (!rLastSelectedPath.empty())
|
|
{
|
|
rLbTree.expand_row(*pEntry);
|
|
if (!rLbTree.iter_children(*pEntry))
|
|
pEntry = nullptr;
|
|
SelectPath(pEntry, rLbTree, rLastSelectedPath);
|
|
}
|
|
break;
|
|
}
|
|
if (!rLbTree.iter_next_sibling(*pEntry))
|
|
pEntry = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
constexpr OUString TG_SETTING_MANAGER = u"TargetInDocument"_ustr;
|
|
constexpr OUString TG_SETTING_LASTMARK = u"LastSelectedMark"_ustr;
|
|
constexpr OUString TG_SETTING_LASTPATH = u"LastSelectedPath"_ustr;
|
|
|
|
void SvxHlinkDlgMarkWnd::RestoreLastSelection()
|
|
{
|
|
bool bSelectedEntry = false;
|
|
|
|
OUString sLastSelectedMark;
|
|
std::deque<OUString> 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<OUString> aTmp;
|
|
aViewSettings.GetUserItem(TG_SETTING_LASTPATH) >>= aTmp;
|
|
aLastSelectedPath = comphelper::sequenceToContainer< std::deque<OUString> >(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<OUString> aTmpSelectedPath(std::move(aLastSelectedPath));
|
|
std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
|
|
if (!mxLbTree->get_iter_first(*xEntry))
|
|
xEntry.reset();
|
|
SelectPath(xEntry.get(), *mxLbTree, aTmpSelectedPath);
|
|
}
|
|
}
|
|
|
|
// Interface to refresh tree
|
|
void SvxHlinkDlgMarkWnd::RefreshTree (const OUString& aStrURL)
|
|
{
|
|
OUString aUStrURL;
|
|
|
|
weld::WaitObject aWait(m_xDialog.get());
|
|
|
|
ClearTree();
|
|
|
|
sal_Int32 nPos = aStrURL.indexOf('#');
|
|
|
|
if (nPos != 0)
|
|
aUStrURL = aStrURL;
|
|
|
|
if (!RefreshFromDoc(aUStrURL))
|
|
ErrorChanged();
|
|
|
|
bool bSelectedEntry = false;
|
|
|
|
if ( nPos != -1 )
|
|
{
|
|
OUString aStrMark = aStrURL.copy(nPos+1);
|
|
bSelectedEntry = SelectEntry(aStrMark);
|
|
}
|
|
|
|
if (!bSelectedEntry)
|
|
RestoreLastSelection();
|
|
}
|
|
|
|
// 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
|
|
if( xDesktop.is() )
|
|
{
|
|
try
|
|
{
|
|
uno::Sequence< beans::PropertyValue > aArg { comphelper::makePropertyValue(u"Hidden"_ustr, true) };
|
|
xComp = xDesktop->loadComponentFromURL( aURL, u"_blank"_ustr, 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, const weld::TreeIter* pParentEntry )
|
|
{
|
|
// used to create the Headings outline parent children tree view relation
|
|
std::stack<std::pair<std::unique_ptr<weld::TreeIter>, const sal_Int32>> aHeadingsParentEntryStack;
|
|
|
|
int nEntries=0;
|
|
|
|
static constexpr OUStringLiteral aProp_LinkDisplayName( u"LinkDisplayName" );
|
|
static constexpr OUStringLiteral aProp_LinkTarget( u"com.sun.star.document.LinkTarget" );
|
|
static constexpr OUStringLiteral aProp_LinkDisplayBitmap( u"LinkDisplayBitmap" );
|
|
for (auto& aLink : xLinks->getElementNames())
|
|
{
|
|
uno::Any aAny;
|
|
|
|
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
|
|
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 );
|
|
OUString sId(weld::toId(pData));
|
|
|
|
std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
|
|
if (pParentEntry)
|
|
{
|
|
OUString sContentType = mxLbTree->get_text(*pParentEntry);
|
|
if (sContentType == "Headings")
|
|
{
|
|
if (aHeadingsParentEntryStack.empty())
|
|
aHeadingsParentEntryStack.push(
|
|
std::pair(mxLbTree->make_iterator(pParentEntry), -1));
|
|
|
|
// get the headings name to display
|
|
aAny = xTarget->getPropertyValue(u"ActualOutlineName"_ustr);
|
|
OUString sActualOutlineName;
|
|
aAny >>= sActualOutlineName;
|
|
|
|
// get the headings outline level
|
|
aAny = xTarget->getPropertyValue(u"OutlineLevel"_ustr);
|
|
sal_Int32 nOutlineLevel = *o3tl::doAccess<sal_Int32>(aAny);
|
|
|
|
// pop until the top of stack entry has an outline level less than
|
|
// the to be inserted heading outline level
|
|
while (nOutlineLevel <= aHeadingsParentEntryStack.top().second)
|
|
aHeadingsParentEntryStack.pop();
|
|
|
|
mxLbTree->insert(aHeadingsParentEntryStack.top().first.get(), -1,
|
|
&sActualOutlineName, &sId, nullptr, nullptr, false,
|
|
xEntry.get());
|
|
|
|
// push if the inserted entry is a child
|
|
if (nOutlineLevel > aHeadingsParentEntryStack.top().second)
|
|
aHeadingsParentEntryStack.push(
|
|
std::pair(mxLbTree->make_iterator(xEntry.get()), nOutlineLevel));
|
|
}
|
|
else
|
|
{
|
|
mxLbTree->insert(pParentEntry, -1, &aStrDisplayname, &sId, nullptr,
|
|
nullptr, false, xEntry.get());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mxLbTree->insert(pParentEntry, -1, &aStrDisplayname, &sId, nullptr, nullptr,
|
|
false, xEntry.get());
|
|
}
|
|
|
|
try
|
|
{
|
|
// get bitmap for the tree-entry
|
|
uno::Reference< awt::XBitmap >
|
|
aXBitmap( xTarget->getPropertyValue( aProp_LinkDisplayBitmap ), uno::UNO_QUERY );
|
|
if (aXBitmap.is())
|
|
{
|
|
Graphic aBmp(Graphic(VCLUnoHelper::GetBitmap(aXBitmap)));
|
|
// insert Displayname into treelist with bitmaps
|
|
mxLbTree->set_image(*xEntry, aBmp.GetXGraphic(), -1);
|
|
}
|
|
}
|
|
catch(const css::uno::Exception&)
|
|
{
|
|
}
|
|
|
|
nEntries++;
|
|
|
|
uno::Reference< document::XLinkTargetSupplier > xLTS( xTarget, uno::UNO_QUERY );
|
|
if( xLTS.is() )
|
|
nEntries += FillTree( xLTS->getLinks(), xEntry.get() );
|
|
}
|
|
catch(const css::uno::Exception&)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
return nEntries;
|
|
}
|
|
|
|
// Clear Tree
|
|
void SvxHlinkDlgMarkWnd::ClearTree()
|
|
{
|
|
std::unique_ptr<weld::TreeIter> xEntry = mxLbTree->make_iterator();
|
|
bool bEntry = mxLbTree->get_iter_first(*xEntry);
|
|
|
|
while (bEntry)
|
|
{
|
|
TargetData* pUserData = weld::fromId<TargetData*>(mxLbTree->get_id(*xEntry));
|
|
delete pUserData;
|
|
|
|
bEntry = mxLbTree->iter_next(*xEntry);
|
|
}
|
|
|
|
mxLbTree->clear();
|
|
}
|
|
|
|
// Find Entry for String
|
|
std::unique_ptr<weld::TreeIter> SvxHlinkDlgMarkWnd::FindEntry (std::u16string_view aStrName)
|
|
{
|
|
bool bFound=false;
|
|
std::unique_ptr<weld::TreeIter> xEntry = mxLbTree->make_iterator();
|
|
bool bEntry = mxLbTree->get_iter_first(*xEntry);
|
|
|
|
while (bEntry && !bFound)
|
|
{
|
|
TargetData* pUserData = weld::fromId<TargetData*>(mxLbTree->get_id(*xEntry));
|
|
if (aStrName == pUserData->aUStrLinkname)
|
|
bFound = true;
|
|
else
|
|
bEntry = mxLbTree->iter_next(*xEntry);
|
|
}
|
|
|
|
if (!bFound)
|
|
xEntry.reset();
|
|
|
|
return xEntry;
|
|
}
|
|
|
|
// Select Entry
|
|
bool SvxHlinkDlgMarkWnd::SelectEntry(std::u16string_view aStrMark)
|
|
{
|
|
std::unique_ptr<weld::TreeIter> xEntry = FindEntry(aStrMark);
|
|
if (!xEntry)
|
|
return false;
|
|
mxLbTree->set_cursor(*xEntry);
|
|
return true;
|
|
}
|
|
|
|
// Click on Apply-Button / Double-click on item in tree
|
|
IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, DoubleClickApplyHdl_Impl, weld::TreeView&, bool)
|
|
{
|
|
ClickApplyHdl_Impl(*mxBtApply);
|
|
return true;
|
|
}
|
|
|
|
IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, ClickApplyHdl_Impl, weld::Button&, void)
|
|
{
|
|
std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
|
|
bool bEntry = mxLbTree->get_cursor(xEntry.get());
|
|
if (bEntry)
|
|
{
|
|
TargetData* pData = weld::fromId<TargetData*>(mxLbTree->get_id(*xEntry));
|
|
if (pData->bIsTarget)
|
|
{
|
|
mpParent->SetMarkStr(pData->aUStrLinkname);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Click on Close-Button
|
|
IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, ClickCloseHdl_Impl, weld::Button&, void)
|
|
{
|
|
std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
|
|
bool bEntry = mxLbTree->get_cursor(xEntry.get());
|
|
if (bEntry)
|
|
{
|
|
TargetData* pUserData = weld::fromId<TargetData*>(mxLbTree->get_id(*xEntry));
|
|
OUString sLastSelectedMark = pUserData->aUStrLinkname;
|
|
|
|
std::deque<OUString> aLastSelectedPath;
|
|
//If the bottommost entry is expanded but nothing
|
|
//underneath it is selected leave a dummy entry
|
|
if (mxLbTree->get_row_expanded(*xEntry))
|
|
aLastSelectedPath.push_front(OUString());
|
|
while (bEntry)
|
|
{
|
|
aLastSelectedPath.push_front(mxLbTree->get_text(*xEntry));
|
|
bEntry = mxLbTree->iter_parent(*xEntry);
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
m_xDialog->response(RET_CANCEL);
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|