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
, u
"cui/ui/hyperlinkmarkdialog.ui"_ustr
, u
"HyperlinkMark"_ustr
)
70 , mpParent(pParentPage
)
71 , mnError(LERR_NOERROR
)
72 , mxBtApply(m_xBuilder
->weld_button(u
"ok"_ustr
))
73 , mxBtClose(m_xBuilder
->weld_button(u
"close"_ustr
))
74 , mxLbTree(m_xBuilder
->weld_tree_view(u
"TreeListBox"_ustr
))
75 , mxError(m_xBuilder
->weld_label(u
"error"_ustr
))
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
) );
83 // tdf#149935 - remember last used position and size
84 SvtViewOptions
aDlgOpt(EViewType::Dialog
, m_xDialog
->get_help_id());
86 m_xDialog
->set_window_state(aDlgOpt
.GetWindowState());
89 SvxHlinkDlgMarkWnd::~SvxHlinkDlgMarkWnd()
92 // tdf#149935 - remember last used position and size
93 SvtViewOptions
aDlgOpt(EViewType::Dialog
, m_xDialog
->get_help_id());
94 aDlgOpt
.SetWindowState(m_xDialog
->get_window_state(vcl::WindowDataMask::PosSize
));
97 void SvxHlinkDlgMarkWnd::ErrorChanged()
99 if (mnError
== LERR_NOENTRIES
)
101 OUString aStrMessage
= CuiResId( RID_CUISTR_HYPDLG_ERR_LERR_NOENTRIES
);
102 mxError
->set_label(aStrMessage
);
106 else if (mnError
== LERR_DOCNOTOPEN
)
108 OUString aStrMessage
= CuiResId( RID_CUISTR_HYPDLG_ERR_LERR_DOCNOTOPEN
);
109 mxError
->set_label(aStrMessage
);
120 // Set an errorstatus
121 sal_uInt16
SvxHlinkDlgMarkWnd::SetError( sal_uInt16 nError
)
123 sal_uInt16 nOldError
= mnError
;
126 if( mnError
!= LERR_NOERROR
)
135 void SvxHlinkDlgMarkWnd::MoveTo(const Point
& rNewPos
)
137 // tdf#149935 - remember last used position and size
138 SvtViewOptions
aDlgOpt(EViewType::Dialog
, m_xDialog
->get_help_id());
139 if (aDlgOpt
.Exists())
140 m_xDialog
->set_window_state(aDlgOpt
.GetWindowState());
142 m_xDialog
->window_move(rNewPos
.X(), rNewPos
.Y());
147 void SelectPath(weld::TreeIter
* pEntry
, weld::TreeView
& rLbTree
,
148 std::deque
<OUString
> &rLastSelectedPath
)
150 OUString
sTitle(rLastSelectedPath
.front());
151 rLastSelectedPath
.pop_front();
152 if (sTitle
.isEmpty())
156 if (sTitle
== rLbTree
.get_text(*pEntry
))
158 rLbTree
.select(*pEntry
);
159 rLbTree
.scroll_to_row(*pEntry
);
160 if (!rLastSelectedPath
.empty())
162 rLbTree
.expand_row(*pEntry
);
163 if (!rLbTree
.iter_children(*pEntry
))
165 SelectPath(pEntry
, rLbTree
, rLastSelectedPath
);
169 if (!rLbTree
.iter_next_sibling(*pEntry
))
175 constexpr OUString TG_SETTING_MANAGER
= u
"TargetInDocument"_ustr
;
176 constexpr OUString TG_SETTING_LASTMARK
= u
"LastSelectedMark"_ustr
;
177 constexpr OUString TG_SETTING_LASTPATH
= u
"LastSelectedPath"_ustr
;
179 void SvxHlinkDlgMarkWnd::RestoreLastSelection()
181 bool bSelectedEntry
= false;
183 OUString sLastSelectedMark
;
184 std::deque
<OUString
> aLastSelectedPath
;
185 SvtViewOptions
aViewSettings( EViewType::Dialog
, TG_SETTING_MANAGER
);
186 if (aViewSettings
.Exists())
188 //Maybe we might want to have some sort of mru list and keep a mapping
189 //per document, rather than the current reuse of "the last thing
190 //selected, regardless of the document"
191 aViewSettings
.GetUserItem(TG_SETTING_LASTMARK
) >>= sLastSelectedMark
;
192 uno::Sequence
<OUString
> aTmp
;
193 aViewSettings
.GetUserItem(TG_SETTING_LASTPATH
) >>= aTmp
;
194 aLastSelectedPath
= comphelper::sequenceToContainer
< std::deque
<OUString
> >(aTmp
);
196 //fallback to previous entry selected the last time we executed this dialog.
197 //First see if the exact mark exists and re-use that
198 if (!sLastSelectedMark
.isEmpty())
199 bSelectedEntry
= SelectEntry(sLastSelectedMark
);
200 //Otherwise just select the closest path available
201 //now to what was available at dialog close time
202 if (!bSelectedEntry
&& !aLastSelectedPath
.empty())
204 std::deque
<OUString
> aTmpSelectedPath(std::move(aLastSelectedPath
));
205 std::unique_ptr
<weld::TreeIter
> xEntry(mxLbTree
->make_iterator());
206 if (!mxLbTree
->get_iter_first(*xEntry
))
208 SelectPath(xEntry
.get(), *mxLbTree
, aTmpSelectedPath
);
212 // Interface to refresh tree
213 void SvxHlinkDlgMarkWnd::RefreshTree (const OUString
& aStrURL
)
217 weld::WaitObject
aWait(m_xDialog
.get());
221 sal_Int32 nPos
= aStrURL
.indexOf('#');
226 if (!RefreshFromDoc(aUStrURL
))
229 bool bSelectedEntry
= false;
233 OUString aStrMark
= aStrURL
.copy(nPos
+1);
234 bSelectedEntry
= SelectEntry(aStrMark
);
238 RestoreLastSelection();
241 // get links from document
242 bool SvxHlinkDlgMarkWnd::RefreshFromDoc(const OUString
& aURL
)
244 mnError
= LERR_NOERROR
;
246 uno::Reference
< frame::XDesktop2
> xDesktop
= frame::Desktop::create( ::comphelper::getProcessComponentContext() );
247 uno::Reference
< lang::XComponent
> xComp
;
249 if( !aURL
.isEmpty() )
256 uno::Sequence
< beans::PropertyValue
> aArg
{ comphelper::makePropertyValue(u
"Hidden"_ustr
, true) };
257 xComp
= xDesktop
->loadComponentFromURL( aURL
, u
"_blank"_ustr
, 0, aArg
);
259 catch( const io::IOException
& )
263 catch( const lang::IllegalArgumentException
& )
271 // the component with user focus ( current document )
272 xComp
= xDesktop
->getCurrentComponent();
277 uno::Reference
< document::XLinkTargetSupplier
> xLTS( xComp
, uno::UNO_QUERY
);
281 if( FillTree( xLTS
->getLinks() ) == 0 )
282 mnError
= LERR_NOENTRIES
;
285 mnError
= LERR_DOCNOTOPEN
;
287 if ( !aURL
.isEmpty() )
292 if( !aURL
.isEmpty() )
293 mnError
=LERR_DOCNOTOPEN
;
299 int SvxHlinkDlgMarkWnd::FillTree( const uno::Reference
< container::XNameAccess
>& xLinks
, const weld::TreeIter
* pParentEntry
)
301 // used to create the Headings outline parent children tree view relation
302 std::stack
<std::pair
<std::unique_ptr
<weld::TreeIter
>, const sal_Int32
>> aHeadingsParentEntryStack
;
306 static constexpr OUStringLiteral
aProp_LinkDisplayName( u
"LinkDisplayName" );
307 static constexpr OUStringLiteral
aProp_LinkTarget( u
"com.sun.star.document.LinkTarget" );
308 static constexpr OUStringLiteral
aProp_LinkDisplayBitmap( u
"LinkDisplayBitmap" );
309 for (auto& aLink
: xLinks
->getElementNames())
315 aAny
= xLinks
->getByName( aLink
);
317 catch(const uno::Exception
&)
319 // if the name of the target was invalid (like empty headings)
320 // no object can be provided
324 uno::Reference
< beans::XPropertySet
> xTarget
;
326 if( aAny
>>= xTarget
)
330 // get name to display
331 aAny
= xTarget
->getPropertyValue( aProp_LinkDisplayName
);
332 OUString aDisplayName
;
333 aAny
>>= aDisplayName
;
334 OUString
aStrDisplayname ( aDisplayName
);
337 uno::Reference
< lang::XServiceInfo
> xSI( xTarget
, uno::UNO_QUERY
);
338 bool bIsTarget
= xSI
->supportsService( aProp_LinkTarget
);
341 TargetData
*pData
= new TargetData ( aLink
, bIsTarget
);
342 OUString
sId(weld::toId(pData
));
344 std::unique_ptr
<weld::TreeIter
> xEntry(mxLbTree
->make_iterator());
347 OUString sContentType
= mxLbTree
->get_text(*pParentEntry
);
348 if (sContentType
== "Headings")
350 if (aHeadingsParentEntryStack
.empty())
351 aHeadingsParentEntryStack
.push(
352 std::pair(mxLbTree
->make_iterator(pParentEntry
), -1));
354 // get the headings name to display
355 aAny
= xTarget
->getPropertyValue(u
"ActualOutlineName"_ustr
);
356 OUString sActualOutlineName
;
357 aAny
>>= sActualOutlineName
;
359 // get the headings outline level
360 aAny
= xTarget
->getPropertyValue(u
"OutlineLevel"_ustr
);
361 sal_Int32 nOutlineLevel
= *o3tl::doAccess
<sal_Int32
>(aAny
);
363 // pop until the top of stack entry has an outline level less than
364 // the to be inserted heading outline level
365 while (nOutlineLevel
<= aHeadingsParentEntryStack
.top().second
)
366 aHeadingsParentEntryStack
.pop();
368 mxLbTree
->insert(aHeadingsParentEntryStack
.top().first
.get(), -1,
369 &sActualOutlineName
, &sId
, nullptr, nullptr, false,
372 // push if the inserted entry is a child
373 if (nOutlineLevel
> aHeadingsParentEntryStack
.top().second
)
374 aHeadingsParentEntryStack
.push(
375 std::pair(mxLbTree
->make_iterator(xEntry
.get()), nOutlineLevel
));
379 mxLbTree
->insert(pParentEntry
, -1, &aStrDisplayname
, &sId
, nullptr,
380 nullptr, false, xEntry
.get());
385 mxLbTree
->insert(pParentEntry
, -1, &aStrDisplayname
, &sId
, nullptr, nullptr,
386 false, xEntry
.get());
391 // get bitmap for the tree-entry
392 uno::Reference
< awt::XBitmap
>
393 aXBitmap( xTarget
->getPropertyValue( aProp_LinkDisplayBitmap
), uno::UNO_QUERY
);
396 Graphic
aBmp(Graphic(VCLUnoHelper::GetBitmap(aXBitmap
)));
397 // insert Displayname into treelist with bitmaps
398 mxLbTree
->set_image(*xEntry
, aBmp
.GetXGraphic(), -1);
401 catch(const css::uno::Exception
&)
407 uno::Reference
< document::XLinkTargetSupplier
> xLTS( xTarget
, uno::UNO_QUERY
);
409 nEntries
+= FillTree( xLTS
->getLinks(), xEntry
.get() );
411 catch(const css::uno::Exception
&)
421 void SvxHlinkDlgMarkWnd::ClearTree()
423 std::unique_ptr
<weld::TreeIter
> xEntry
= mxLbTree
->make_iterator();
424 bool bEntry
= mxLbTree
->get_iter_first(*xEntry
);
428 TargetData
* pUserData
= weld::fromId
<TargetData
*>(mxLbTree
->get_id(*xEntry
));
431 bEntry
= mxLbTree
->iter_next(*xEntry
);
437 // Find Entry for String
438 std::unique_ptr
<weld::TreeIter
> SvxHlinkDlgMarkWnd::FindEntry (std::u16string_view aStrName
)
441 std::unique_ptr
<weld::TreeIter
> xEntry
= mxLbTree
->make_iterator();
442 bool bEntry
= mxLbTree
->get_iter_first(*xEntry
);
444 while (bEntry
&& !bFound
)
446 TargetData
* pUserData
= weld::fromId
<TargetData
*>(mxLbTree
->get_id(*xEntry
));
447 if (aStrName
== pUserData
->aUStrLinkname
)
450 bEntry
= mxLbTree
->iter_next(*xEntry
);
460 bool SvxHlinkDlgMarkWnd::SelectEntry(std::u16string_view aStrMark
)
462 std::unique_ptr
<weld::TreeIter
> xEntry
= FindEntry(aStrMark
);
465 mxLbTree
->set_cursor(*xEntry
);
469 // Click on Apply-Button / Double-click on item in tree
470 IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd
, DoubleClickApplyHdl_Impl
, weld::TreeView
&, bool)
472 ClickApplyHdl_Impl(*mxBtApply
);
476 IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd
, ClickApplyHdl_Impl
, weld::Button
&, void)
478 std::unique_ptr
<weld::TreeIter
> xEntry(mxLbTree
->make_iterator());
479 bool bEntry
= mxLbTree
->get_cursor(xEntry
.get());
482 TargetData
* pData
= weld::fromId
<TargetData
*>(mxLbTree
->get_id(*xEntry
));
483 if (pData
->bIsTarget
)
485 mpParent
->SetMarkStr(pData
->aUStrLinkname
);
490 // Click on Close-Button
491 IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd
, ClickCloseHdl_Impl
, weld::Button
&, void)
493 std::unique_ptr
<weld::TreeIter
> xEntry(mxLbTree
->make_iterator());
494 bool bEntry
= mxLbTree
->get_cursor(xEntry
.get());
497 TargetData
* pUserData
= weld::fromId
<TargetData
*>(mxLbTree
->get_id(*xEntry
));
498 OUString sLastSelectedMark
= pUserData
->aUStrLinkname
;
500 std::deque
<OUString
> aLastSelectedPath
;
501 //If the bottommost entry is expanded but nothing
502 //underneath it is selected leave a dummy entry
503 if (mxLbTree
->get_row_expanded(*xEntry
))
504 aLastSelectedPath
.push_front(OUString());
507 aLastSelectedPath
.push_front(mxLbTree
->get_text(*xEntry
));
508 bEntry
= mxLbTree
->iter_parent(*xEntry
);
511 uno::Sequence
< beans::NamedValue
> aSettings
513 { TG_SETTING_LASTMARK
, css::uno::Any(sLastSelectedMark
) },
514 { TG_SETTING_LASTPATH
, css::uno::Any(comphelper::containerToSequence(aLastSelectedPath
)) }
518 SvtViewOptions
aViewSettings( EViewType::Dialog
, TG_SETTING_MANAGER
);
519 aViewSettings
.SetUserData( aSettings
);
522 m_xDialog
->response(RET_CANCEL
);
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */