1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <dialmgr.hxx>
21 #include <o3tl/any.hxx>
22 #include <comphelper/propertyvalue.hxx>
23 #include <unotools/viewoptions.hxx>
24 #include <vcl/graph.hxx>
27 #include <comphelper/processfactory.hxx>
28 #include <comphelper/sequence.hxx>
29 #include <com/sun/star/awt/XBitmap.hpp>
30 #include <com/sun/star/frame/Desktop.hpp>
31 #include <com/sun/star/beans/NamedValue.hpp>
32 #include <com/sun/star/beans/PropertyValue.hpp>
33 #include <com/sun/star/document/XLinkTargetSupplier.hpp>
34 #include <com/sun/star/beans/XPropertySet.hpp>
35 #include <com/sun/star/io/IOException.hpp>
37 #include <toolkit/helper/vclunohelper.hxx>
39 #include <strings.hrc>
40 #include <hlmarkwn.hxx>
41 #include <hltpbase.hxx>
42 #include <hlmarkwn_def.hxx>
46 using namespace ::com::sun::star
;
50 // Userdata-struct for tree-entries
53 OUString aUStrLinkname
;
56 TargetData (const OUString
& aUStrLName
, bool bTarget
)
60 aUStrLinkname
= aUStrLName
;
66 //*** Window-Class ***
67 // Constructor / Destructor
68 SvxHlinkDlgMarkWnd::SvxHlinkDlgMarkWnd(weld::Window
* pParentDialog
, SvxHyperlinkTabPageBase
*pParentPage
)
69 : GenericDialogController(pParentDialog
, "cui/ui/hyperlinkmarkdialog.ui", "HyperlinkMark")
70 , mpParent(pParentPage
)
71 , mnError(LERR_NOERROR
)
72 , mxBtApply(m_xBuilder
->weld_button("ok"))
73 , mxBtClose(m_xBuilder
->weld_button("close"))
74 , mxLbTree(m_xBuilder
->weld_tree_view("TreeListBox"))
75 , mxError(m_xBuilder
->weld_label("error"))
77 mxLbTree
->set_size_request(mxLbTree
->get_approximate_digit_width() * 25,
78 mxLbTree
->get_height_rows(12));
79 mxBtApply
->connect_clicked( LINK ( this, SvxHlinkDlgMarkWnd
, ClickApplyHdl_Impl
) );
80 mxBtClose
->connect_clicked( LINK ( this, SvxHlinkDlgMarkWnd
, ClickCloseHdl_Impl
) );
81 mxLbTree
->connect_row_activated( LINK ( this, SvxHlinkDlgMarkWnd
, DoubleClickApplyHdl_Impl
) );
84 SvxHlinkDlgMarkWnd::~SvxHlinkDlgMarkWnd()
89 void SvxHlinkDlgMarkWnd::ErrorChanged()
91 if (mnError
== LERR_NOENTRIES
)
93 OUString aStrMessage
= CuiResId( RID_CUISTR_HYPDLG_ERR_LERR_NOENTRIES
);
94 mxError
->set_label(aStrMessage
);
98 else if (mnError
== LERR_DOCNOTOPEN
)
100 OUString aStrMessage
= CuiResId( RID_CUISTR_HYPDLG_ERR_LERR_DOCNOTOPEN
);
101 mxError
->set_label(aStrMessage
);
112 // Set an errorstatus
113 sal_uInt16
SvxHlinkDlgMarkWnd::SetError( sal_uInt16 nError
)
115 sal_uInt16 nOldError
= mnError
;
118 if( mnError
!= LERR_NOERROR
)
127 void SvxHlinkDlgMarkWnd::MoveTo(const Point
& rNewPos
)
129 m_xDialog
->window_move(rNewPos
.X(), rNewPos
.Y());
134 void SelectPath(weld::TreeIter
* pEntry
, weld::TreeView
& rLbTree
,
135 std::deque
<OUString
> &rLastSelectedPath
)
137 OUString
sTitle(rLastSelectedPath
.front());
138 rLastSelectedPath
.pop_front();
139 if (sTitle
.isEmpty())
143 if (sTitle
== rLbTree
.get_text(*pEntry
))
145 rLbTree
.select(*pEntry
);
146 rLbTree
.scroll_to_row(*pEntry
);
147 if (!rLastSelectedPath
.empty())
149 rLbTree
.expand_row(*pEntry
);
150 if (!rLbTree
.iter_children(*pEntry
))
152 SelectPath(pEntry
, rLbTree
, rLastSelectedPath
);
156 if (!rLbTree
.iter_next_sibling(*pEntry
))
162 constexpr OUStringLiteral TG_SETTING_MANAGER
= u
"TargetInDocument";
163 constexpr OUStringLiteral TG_SETTING_LASTMARK
= u
"LastSelectedMark";
164 constexpr OUStringLiteral TG_SETTING_LASTPATH
= u
"LastSelectedPath";
166 void SvxHlinkDlgMarkWnd::RestoreLastSelection()
168 bool bSelectedEntry
= false;
170 OUString sLastSelectedMark
;
171 std::deque
<OUString
> aLastSelectedPath
;
172 SvtViewOptions
aViewSettings( EViewType::Dialog
, TG_SETTING_MANAGER
);
173 if (aViewSettings
.Exists())
175 //Maybe we might want to have some sort of mru list and keep a mapping
176 //per document, rather than the current reuse of "the last thing
177 //selected, regardless of the document"
178 aViewSettings
.GetUserItem(TG_SETTING_LASTMARK
) >>= sLastSelectedMark
;
179 uno::Sequence
<OUString
> aTmp
;
180 aViewSettings
.GetUserItem(TG_SETTING_LASTPATH
) >>= aTmp
;
181 aLastSelectedPath
= comphelper::sequenceToContainer
< std::deque
<OUString
> >(aTmp
);
183 //fallback to previous entry selected the last time we executed this dialog.
184 //First see if the exact mark exists and re-use that
185 if (!sLastSelectedMark
.isEmpty())
186 bSelectedEntry
= SelectEntry(sLastSelectedMark
);
187 //Otherwise just select the closest path available
188 //now to what was available at dialog close time
189 if (!bSelectedEntry
&& !aLastSelectedPath
.empty())
191 std::deque
<OUString
> aTmpSelectedPath(aLastSelectedPath
);
192 std::unique_ptr
<weld::TreeIter
> xEntry(mxLbTree
->make_iterator());
193 if (!mxLbTree
->get_iter_first(*xEntry
))
195 SelectPath(xEntry
.get(), *mxLbTree
, aTmpSelectedPath
);
199 // Interface to refresh tree
200 void SvxHlinkDlgMarkWnd::RefreshTree (const OUString
& aStrURL
)
204 weld::WaitObject
aWait(m_xDialog
.get());
208 sal_Int32 nPos
= aStrURL
.indexOf('#');
213 if (!RefreshFromDoc(aUStrURL
))
216 bool bSelectedEntry
= false;
220 OUString aStrMark
= aStrURL
.copy(nPos
+1);
221 bSelectedEntry
= SelectEntry(aStrMark
);
225 RestoreLastSelection();
228 // get links from document
229 bool SvxHlinkDlgMarkWnd::RefreshFromDoc(const OUString
& aURL
)
231 mnError
= LERR_NOERROR
;
233 uno::Reference
< frame::XDesktop2
> xDesktop
= frame::Desktop::create( ::comphelper::getProcessComponentContext() );
234 uno::Reference
< lang::XComponent
> xComp
;
236 if( !aURL
.isEmpty() )
243 uno::Sequence
< beans::PropertyValue
> aArg
{ comphelper::makePropertyValue("Hidden", true) };
244 xComp
= xDesktop
->loadComponentFromURL( aURL
, "_blank", 0, aArg
);
246 catch( const io::IOException
& )
250 catch( const lang::IllegalArgumentException
& )
258 // the component with user focus ( current document )
259 xComp
= xDesktop
->getCurrentComponent();
264 uno::Reference
< document::XLinkTargetSupplier
> xLTS( xComp
, uno::UNO_QUERY
);
268 if( FillTree( xLTS
->getLinks() ) == 0 )
269 mnError
= LERR_NOENTRIES
;
272 mnError
= LERR_DOCNOTOPEN
;
274 if ( !aURL
.isEmpty() )
279 if( !aURL
.isEmpty() )
280 mnError
=LERR_DOCNOTOPEN
;
286 int SvxHlinkDlgMarkWnd::FillTree( const uno::Reference
< container::XNameAccess
>& xLinks
, const weld::TreeIter
* pParentEntry
)
288 // used to create the Headings outline parent children tree view relation
289 std::stack
<std::pair
<std::unique_ptr
<weld::TreeIter
>, const sal_Int32
>> aHeadingsParentEntryStack
;
292 const uno::Sequence
< OUString
> aNames( xLinks
->getElementNames() );
293 const sal_Int32 nLinks
= aNames
.getLength();
294 const OUString
* pNames
= aNames
.getConstArray();
296 static const OUStringLiteral
aProp_LinkDisplayName( u
"LinkDisplayName" );
297 static const OUStringLiteral
aProp_LinkTarget( u
"com.sun.star.document.LinkTarget" );
298 static const OUStringLiteral
aProp_LinkDisplayBitmap( u
"LinkDisplayBitmap" );
299 for( sal_Int32 i
= 0; i
< nLinks
; i
++ )
302 OUString
aLink( *pNames
++ );
307 aAny
= xLinks
->getByName( aLink
);
309 catch(const uno::Exception
&)
311 // if the name of the target was invalid (like empty headings)
312 // no object can be provided
318 uno::Reference
< beans::XPropertySet
> xTarget
;
320 if( aAny
>>= xTarget
)
324 // get name to display
325 aAny
= xTarget
->getPropertyValue( aProp_LinkDisplayName
);
326 OUString aDisplayName
;
327 aAny
>>= aDisplayName
;
328 OUString
aStrDisplayname ( aDisplayName
);
331 uno::Reference
< lang::XServiceInfo
> xSI( xTarget
, uno::UNO_QUERY
);
332 bool bIsTarget
= xSI
->supportsService( aProp_LinkTarget
);
335 TargetData
*pData
= new TargetData ( aLink
, bIsTarget
);
336 OUString
sId(weld::toId(pData
));
338 std::unique_ptr
<weld::TreeIter
> xEntry(mxLbTree
->make_iterator());
341 OUString sContentType
= mxLbTree
->get_text(*pParentEntry
);
342 if (sContentType
== "Headings")
344 if (aHeadingsParentEntryStack
.empty())
345 aHeadingsParentEntryStack
.push(
346 std::pair(mxLbTree
->make_iterator(pParentEntry
), -1));
348 // get the headings name to display
349 aAny
= xTarget
->getPropertyValue("ActualOutlineName");
350 OUString sActualOutlineName
;
351 aAny
>>= sActualOutlineName
;
353 // get the headings outline level
354 aAny
= xTarget
->getPropertyValue("OutlineLevel");
355 sal_Int32 nOutlineLevel
= *o3tl::doAccess
<sal_Int32
>(aAny
);
357 // pop until the top of stack entry has an outline level less than
358 // the to be inserted heading outline level
359 while (nOutlineLevel
<= aHeadingsParentEntryStack
.top().second
)
360 aHeadingsParentEntryStack
.pop();
362 mxLbTree
->insert(aHeadingsParentEntryStack
.top().first
.get(), -1,
363 &sActualOutlineName
, &sId
, nullptr, nullptr, false,
366 // push if the inserted entry is a child
367 if (nOutlineLevel
> aHeadingsParentEntryStack
.top().second
)
368 aHeadingsParentEntryStack
.push(
369 std::pair(mxLbTree
->make_iterator(xEntry
.get()), nOutlineLevel
));
373 mxLbTree
->insert(pParentEntry
, -1, &aStrDisplayname
, &sId
, nullptr,
374 nullptr, false, xEntry
.get());
379 mxLbTree
->insert(pParentEntry
, -1, &aStrDisplayname
, &sId
, nullptr, nullptr,
380 false, xEntry
.get());
385 // get bitmap for the tree-entry
386 uno::Reference
< awt::XBitmap
>
387 aXBitmap( xTarget
->getPropertyValue( aProp_LinkDisplayBitmap
), uno::UNO_QUERY
);
390 Graphic
aBmp(Graphic(VCLUnoHelper::GetBitmap(aXBitmap
)));
391 // insert Displayname into treelist with bitmaps
392 mxLbTree
->set_image(*xEntry
, aBmp
.GetXGraphic(), -1);
395 catch(const css::uno::Exception
&)
401 uno::Reference
< document::XLinkTargetSupplier
> xLTS( xTarget
, uno::UNO_QUERY
);
403 nEntries
+= FillTree( xLTS
->getLinks(), xEntry
.get() );
405 catch(const css::uno::Exception
&)
415 void SvxHlinkDlgMarkWnd::ClearTree()
417 std::unique_ptr
<weld::TreeIter
> xEntry
= mxLbTree
->make_iterator();
418 bool bEntry
= mxLbTree
->get_iter_first(*xEntry
);
422 TargetData
* pUserData
= weld::fromId
<TargetData
*>(mxLbTree
->get_id(*xEntry
));
425 bEntry
= mxLbTree
->iter_next(*xEntry
);
431 // Find Entry for String
432 std::unique_ptr
<weld::TreeIter
> SvxHlinkDlgMarkWnd::FindEntry (std::u16string_view aStrName
)
435 std::unique_ptr
<weld::TreeIter
> xEntry
= mxLbTree
->make_iterator();
436 bool bEntry
= mxLbTree
->get_iter_first(*xEntry
);
438 while (bEntry
&& !bFound
)
440 TargetData
* pUserData
= weld::fromId
<TargetData
*>(mxLbTree
->get_id(*xEntry
));
441 if (aStrName
== pUserData
->aUStrLinkname
)
444 bEntry
= mxLbTree
->iter_next(*xEntry
);
454 bool SvxHlinkDlgMarkWnd::SelectEntry(std::u16string_view aStrMark
)
456 std::unique_ptr
<weld::TreeIter
> xEntry
= FindEntry(aStrMark
);
459 mxLbTree
->set_cursor(*xEntry
);
463 // Click on Apply-Button / Double-click on item in tree
464 IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd
, DoubleClickApplyHdl_Impl
, weld::TreeView
&, bool)
466 ClickApplyHdl_Impl(*mxBtApply
);
470 IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd
, ClickApplyHdl_Impl
, weld::Button
&, void)
472 std::unique_ptr
<weld::TreeIter
> xEntry(mxLbTree
->make_iterator());
473 bool bEntry
= mxLbTree
->get_cursor(xEntry
.get());
476 TargetData
* pData
= weld::fromId
<TargetData
*>(mxLbTree
->get_id(*xEntry
));
477 if (pData
->bIsTarget
)
479 mpParent
->SetMarkStr(pData
->aUStrLinkname
);
484 // Click on Close-Button
485 IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd
, ClickCloseHdl_Impl
, weld::Button
&, void)
487 std::unique_ptr
<weld::TreeIter
> xEntry(mxLbTree
->make_iterator());
488 bool bEntry
= mxLbTree
->get_cursor(xEntry
.get());
491 TargetData
* pUserData
= weld::fromId
<TargetData
*>(mxLbTree
->get_id(*xEntry
));
492 OUString sLastSelectedMark
= pUserData
->aUStrLinkname
;
494 std::deque
<OUString
> aLastSelectedPath
;
495 //If the bottommost entry is expanded but nothing
496 //underneath it is selected leave a dummy entry
497 if (mxLbTree
->get_row_expanded(*xEntry
))
498 aLastSelectedPath
.push_front(OUString());
501 aLastSelectedPath
.push_front(mxLbTree
->get_text(*xEntry
));
502 bEntry
= mxLbTree
->iter_parent(*xEntry
);
505 uno::Sequence
< beans::NamedValue
> aSettings
507 { TG_SETTING_LASTMARK
, css::uno::Any(sLastSelectedMark
) },
508 { TG_SETTING_LASTPATH
, css::uno::Any(comphelper::containerToSequence(aLastSelectedPath
)) }
512 SvtViewOptions
aViewSettings( EViewType::Dialog
, TG_SETTING_MANAGER
);
513 aViewSettings
.SetUserData( aSettings
);
516 m_xDialog
->response(RET_CANCEL
);
519 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */