/* -*- 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 <sfx2/docfile.hxx>
#include <unotools/viewoptions.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/wrkwin.hxx>
#include <vcl/builderfactory.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/frame/XComponentLoader.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 <svtools/treelistentry.hxx>
#include <strings.hrc>
#include <hlmarkwn.hxx>
#include <hltpbase.hxx>
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<OUString> &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<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(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();
maStrLastURL = aStrURL;
}
// 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<void*>(pData) );
nEntries++;
}
else
{
// insert Displayname into treelist without bitmaps
pEntry = mpLbTree->InsertEntry ( aStrDisplayname,
pParentEntry,
false, TREELIST_APPEND,
static_cast<void*>(pData) );
nEntries++;
}
}
catch(const css::uno::Exception&)
{
// insert Displayname into treelist without bitmaps
pEntry = mpLbTree->InsertEntry ( aStrDisplayname,
pParentEntry,
false, TREELIST_APPEND,
static_cast<void*>(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<TargetData *>(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<TargetData *>(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<TargetData *>(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<TargetData *>(pEntry->GetUserData());
OUString sLastSelectedMark = pUserData->aUStrLinkname;
std::deque<OUString> 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: */
↑ V547 Expression 'pEntry' is always true.