LibreOffice Module cui (master)  1
hlmarkwn.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <dialmgr.hxx>
21 #include <o3tl/any.hxx>
23 #include <unotools/viewoptions.hxx>
24 #include <vcl/graph.hxx>
25 
26 // UNO-Stuff
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>
36 
38 
39 #include <strings.hrc>
40 #include <hlmarkwn.hxx>
41 #include <hltpbase.hxx>
42 #include <hlmarkwn_def.hxx>
43 
44 #include <stack>
45 
46 using namespace ::com::sun::star;
47 
48 namespace {
49 
50 // Userdata-struct for tree-entries
51 struct TargetData
52 {
53  OUString aUStrLinkname;
54  bool bIsTarget;
55 
56  TargetData (const OUString& aUStrLName, bool bTarget)
57  : bIsTarget(bTarget)
58  {
59  if (bIsTarget)
60  aUStrLinkname = aUStrLName;
61  }
62 };
63 
64 }
65 
66 //*** Window-Class ***
67 // Constructor / Destructor
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"))
76 {
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 ) );
82 }
83 
85 {
86  ClearTree();
87 }
88 
90 {
91  if (mnError == LERR_NOENTRIES)
92  {
93  OUString aStrMessage = CuiResId( RID_CUISTR_HYPDLG_ERR_LERR_NOENTRIES );
94  mxError->set_label(aStrMessage);
95  mxError->show();
96  mxLbTree->hide();
97  }
98  else if (mnError == LERR_DOCNOTOPEN)
99  {
100  OUString aStrMessage = CuiResId( RID_CUISTR_HYPDLG_ERR_LERR_DOCNOTOPEN );
101  mxError->set_label(aStrMessage);
102  mxError->show();
103  mxLbTree->hide();
104  }
105  else
106  {
107  mxLbTree->show();
108  mxError->hide();
109  }
110 }
111 
112 // Set an errorstatus
113 sal_uInt16 SvxHlinkDlgMarkWnd::SetError( sal_uInt16 nError)
114 {
115  sal_uInt16 nOldError = mnError;
116  mnError = nError;
117 
118  if( mnError != LERR_NOERROR )
119  ClearTree();
120 
121  ErrorChanged();
122 
123  return nOldError;
124 }
125 
126 // Move window
127 void SvxHlinkDlgMarkWnd::MoveTo(const Point& rNewPos)
128 {
129  m_xDialog->window_move(rNewPos.X(), rNewPos.Y());
130 }
131 
132 namespace
133 {
134  void SelectPath(weld::TreeIter* pEntry, weld::TreeView& rLbTree,
135  std::deque<OUString> &rLastSelectedPath)
136  {
137  OUString sTitle(rLastSelectedPath.front());
138  rLastSelectedPath.pop_front();
139  if (sTitle.isEmpty())
140  return;
141  while (pEntry)
142  {
143  if (sTitle == rLbTree.get_text(*pEntry))
144  {
145  rLbTree.select(*pEntry);
146  rLbTree.scroll_to_row(*pEntry);
147  if (!rLastSelectedPath.empty())
148  {
149  rLbTree.expand_row(*pEntry);
150  if (!rLbTree.iter_children(*pEntry))
151  pEntry = nullptr;
152  SelectPath(pEntry, rLbTree, rLastSelectedPath);
153  }
154  break;
155  }
156  if (!rLbTree.iter_next_sibling(*pEntry))
157  pEntry = nullptr;
158  }
159  }
160 }
161 
162 constexpr OUStringLiteral TG_SETTING_MANAGER = u"TargetInDocument";
163 constexpr OUStringLiteral TG_SETTING_LASTMARK = u"LastSelectedMark";
164 constexpr OUStringLiteral TG_SETTING_LASTPATH = u"LastSelectedPath";
165 
167 {
168  bool bSelectedEntry = false;
169 
170  OUString sLastSelectedMark;
171  std::deque<OUString> aLastSelectedPath;
172  SvtViewOptions aViewSettings( EViewType::Dialog, TG_SETTING_MANAGER );
173  if (aViewSettings.Exists())
174  {
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);
182  }
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())
190  {
191  std::deque<OUString> aTmpSelectedPath(aLastSelectedPath);
192  std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
193  if (!mxLbTree->get_iter_first(*xEntry))
194  xEntry.reset();
195  SelectPath(xEntry.get(), *mxLbTree, aTmpSelectedPath);
196  }
197 }
198 
199 // Interface to refresh tree
200 void SvxHlinkDlgMarkWnd::RefreshTree (const OUString& aStrURL)
201 {
202  OUString aUStrURL;
203 
204  weld::WaitObject aWait(m_xDialog.get());
205 
206  ClearTree();
207 
208  sal_Int32 nPos = aStrURL.indexOf('#');
209 
210  if (nPos != 0)
211  aUStrURL = aStrURL;
212 
213  if (!RefreshFromDoc(aUStrURL))
214  ErrorChanged();
215 
216  bool bSelectedEntry = false;
217 
218  if ( nPos != -1 )
219  {
220  OUString aStrMark = aStrURL.copy(nPos+1);
221  bSelectedEntry = SelectEntry(aStrMark);
222  }
223 
224  if (!bSelectedEntry)
226 }
227 
228 // get links from document
229 bool SvxHlinkDlgMarkWnd::RefreshFromDoc(const OUString& aURL)
230 {
232 
233  uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( ::comphelper::getProcessComponentContext() );
234  uno::Reference< lang::XComponent > xComp;
235 
236  if( !aURL.isEmpty() )
237  {
238  // load from url
239  if( xDesktop.is() )
240  {
241  try
242  {
243  uno::Sequence< beans::PropertyValue > aArg { comphelper::makePropertyValue("Hidden", true) };
244  xComp = xDesktop->loadComponentFromURL( aURL, "_blank", 0, aArg );
245  }
246  catch( const io::IOException& )
247  {
248 
249  }
250  catch( const lang::IllegalArgumentException& )
251  {
252 
253  }
254  }
255  }
256  else
257  {
258  // the component with user focus ( current document )
259  xComp = xDesktop->getCurrentComponent();
260  }
261 
262  if( xComp.is() )
263  {
264  uno::Reference< document::XLinkTargetSupplier > xLTS( xComp, uno::UNO_QUERY );
265 
266  if( xLTS.is() )
267  {
268  if( FillTree( xLTS->getLinks() ) == 0 )
270  }
271  else
273 
274  if ( !aURL.isEmpty() )
275  xComp->dispose();
276  }
277  else
278  {
279  if( !aURL.isEmpty() )
281  }
282  return (mnError==0);
283 }
284 
285 // Fill Tree-Control
286 int SvxHlinkDlgMarkWnd::FillTree( const uno::Reference< container::XNameAccess >& xLinks, const weld::TreeIter* pParentEntry )
287 {
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;
290 
291  int nEntries=0;
292  const uno::Sequence< OUString > aNames( xLinks->getElementNames() );
293  const sal_Int32 nLinks = aNames.getLength();
294  const OUString* pNames = aNames.getConstArray();
295 
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++ )
300  {
301  uno::Any aAny;
302  OUString aLink( *pNames++ );
303 
304  bool bError = false;
305  try
306  {
307  aAny = xLinks->getByName( aLink );
308  }
309  catch(const uno::Exception&)
310  {
311  // if the name of the target was invalid (like empty headings)
312  // no object can be provided
313  bError = true;
314  }
315  if(bError)
316  continue;
317 
318  uno::Reference< beans::XPropertySet > xTarget;
319 
320  if( aAny >>= xTarget )
321  {
322  try
323  {
324  // get name to display
325  aAny = xTarget->getPropertyValue( aProp_LinkDisplayName );
326  OUString aDisplayName;
327  aAny >>= aDisplayName;
328  OUString aStrDisplayname ( aDisplayName );
329 
330  // is it a target ?
331  uno::Reference< lang::XServiceInfo > xSI( xTarget, uno::UNO_QUERY );
332  bool bIsTarget = xSI->supportsService( aProp_LinkTarget );
333 
334  // create userdata
335  TargetData *pData = new TargetData ( aLink, bIsTarget );
336  OUString sId(weld::toId(pData));
337 
338  std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
339  if (pParentEntry)
340  {
341  OUString sContentType = mxLbTree->get_text(*pParentEntry);
342  if (sContentType == "Headings")
343  {
344  if (aHeadingsParentEntryStack.empty())
345  aHeadingsParentEntryStack.push(
346  std::pair(mxLbTree->make_iterator(pParentEntry), -1));
347 
348  // get the headings name to display
349  aAny = xTarget->getPropertyValue("ActualOutlineName");
350  OUString sActualOutlineName;
351  aAny >>= sActualOutlineName;
352 
353  // get the headings outline level
354  aAny = xTarget->getPropertyValue("OutlineLevel");
355  sal_Int32 nOutlineLevel = *o3tl::doAccess<sal_Int32>(aAny);
356 
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();
361 
362  mxLbTree->insert(aHeadingsParentEntryStack.top().first.get(), -1,
363  &sActualOutlineName, &sId, nullptr, nullptr, false,
364  xEntry.get());
365 
366  // push if the inserted entry is a child
367  if (nOutlineLevel > aHeadingsParentEntryStack.top().second)
368  aHeadingsParentEntryStack.push(
369  std::pair(std::move(xEntry), nOutlineLevel));
370  }
371  else
372  {
373  mxLbTree->insert(pParentEntry, -1, &aStrDisplayname, &sId, nullptr,
374  nullptr, false, xEntry.get());
375  }
376  }
377  else
378  {
379  mxLbTree->insert(pParentEntry, -1, &aStrDisplayname, &sId, nullptr, nullptr,
380  false, xEntry.get());
381  }
382 
383  try
384  {
385  // get bitmap for the tree-entry
386  uno::Reference< awt::XBitmap >
387  aXBitmap( xTarget->getPropertyValue( aProp_LinkDisplayBitmap ), uno::UNO_QUERY );
388  if (aXBitmap.is())
389  {
390  Graphic aBmp(Graphic(VCLUnoHelper::GetBitmap(aXBitmap)));
391  // insert Displayname into treelist with bitmaps
392  mxLbTree->set_image(*xEntry, aBmp.GetXGraphic(), -1);
393  }
394  }
395  catch(const css::uno::Exception&)
396  {
397  }
398 
399  nEntries++;
400 
401  uno::Reference< document::XLinkTargetSupplier > xLTS( xTarget, uno::UNO_QUERY );
402  if( xLTS.is() )
403  nEntries += FillTree( xLTS->getLinks(), xEntry.get() );
404  }
405  catch(const css::uno::Exception&)
406  {
407  }
408  }
409  }
410 
411  return nEntries;
412 }
413 
414 // Clear Tree
416 {
417  std::unique_ptr<weld::TreeIter> xEntry = mxLbTree->make_iterator();
418  bool bEntry = mxLbTree->get_iter_first(*xEntry);
419 
420  while (bEntry)
421  {
422  TargetData* pUserData = weld::fromId<TargetData*>(mxLbTree->get_id(*xEntry));
423  delete pUserData;
424 
425  bEntry = mxLbTree->iter_next(*xEntry);
426  }
427 
428  mxLbTree->clear();
429 }
430 
431 // Find Entry for String
432 std::unique_ptr<weld::TreeIter> SvxHlinkDlgMarkWnd::FindEntry (std::u16string_view aStrName)
433 {
434  bool bFound=false;
435  std::unique_ptr<weld::TreeIter> xEntry = mxLbTree->make_iterator();
436  bool bEntry = mxLbTree->get_iter_first(*xEntry);
437 
438  while (bEntry && !bFound)
439  {
440  TargetData* pUserData = weld::fromId<TargetData*>(mxLbTree->get_id(*xEntry));
441  if (aStrName == pUserData->aUStrLinkname)
442  bFound = true;
443  else
444  bEntry = mxLbTree->iter_next(*xEntry);
445  }
446 
447  if (!bFound)
448  xEntry.reset();
449 
450  return xEntry;
451 }
452 
453 // Select Entry
454 bool SvxHlinkDlgMarkWnd::SelectEntry(std::u16string_view aStrMark)
455 {
456  std::unique_ptr<weld::TreeIter> xEntry = FindEntry(aStrMark);
457  if (!xEntry)
458  return false;
459  mxLbTree->select(*xEntry);
460  mxLbTree->scroll_to_row(*xEntry);
461  return true;
462 }
463 
464 // Click on Apply-Button / Double-click on item in tree
465 IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, DoubleClickApplyHdl_Impl, weld::TreeView&, bool)
466 {
467  ClickApplyHdl_Impl(*mxBtApply);
468  return true;
469 }
470 
471 IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, ClickApplyHdl_Impl, weld::Button&, void)
472 {
473  std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
474  bool bEntry = mxLbTree->get_cursor(xEntry.get());
475  if (bEntry)
476  {
477  TargetData* pData = weld::fromId<TargetData*>(mxLbTree->get_id(*xEntry));
478  if (pData->bIsTarget)
479  {
480  mpParent->SetMarkStr(pData->aUStrLinkname);
481  }
482  }
483 }
484 
485 // Click on Close-Button
486 IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, ClickCloseHdl_Impl, weld::Button&, void)
487 {
488  std::unique_ptr<weld::TreeIter> xEntry(mxLbTree->make_iterator());
489  bool bEntry = mxLbTree->get_cursor(xEntry.get());
490  if (bEntry)
491  {
492  TargetData* pUserData = weld::fromId<TargetData*>(mxLbTree->get_id(*xEntry));
493  OUString sLastSelectedMark = pUserData->aUStrLinkname;
494 
495  std::deque<OUString> aLastSelectedPath;
496  //If the bottommost entry is expanded but nothing
497  //underneath it is selected leave a dummy entry
498  if (mxLbTree->get_row_expanded(*xEntry))
499  aLastSelectedPath.push_front(OUString());
500  while (bEntry)
501  {
502  aLastSelectedPath.push_front(mxLbTree->get_text(*xEntry));
503  bEntry = mxLbTree->iter_parent(*xEntry);
504  }
505 
506  uno::Sequence< beans::NamedValue > aSettings
507  {
508  { TG_SETTING_LASTMARK, css::uno::Any(sLastSelectedMark) },
509  { TG_SETTING_LASTPATH, css::uno::Any(comphelper::containerToSequence(aLastSelectedPath)) }
510  };
511 
512  // write
513  SvtViewOptions aViewSettings( EViewType::Dialog, TG_SETTING_MANAGER );
514  aViewSettings.SetUserData( aSettings );
515  }
516 
517  m_xDialog->response(RET_CANCEL);
518 }
519 
520 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::shared_ptr< weld::Dialog > m_xDialog
std::unique_ptr< weld::Button > mxBtApply
Definition: hlmarkwn.hxx:37
constexpr OUStringLiteral TG_SETTING_LASTPATH
Definition: hlmarkwn.cxx:164
std::unique_ptr< sal_Int32[]> pData
int FillTree(const css::uno::Reference< css::container::XNameAccess > &xLinks, const weld::TreeIter *pParentEntry=nullptr)
Definition: hlmarkwn.cxx:286
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
virtual ~SvxHlinkDlgMarkWnd() override
Definition: hlmarkwn.cxx:84
Reference< XInterface > xTarget
#define LERR_NOENTRIES
void MoveTo(const Point &rNewPos)
Definition: hlmarkwn.cxx:127
std::unique_ptr< weld::TreeIter > FindEntry(std::u16string_view aStrName)
Definition: hlmarkwn.cxx:432
RET_CANCEL
void RefreshTree(const OUString &aStrURL)
Definition: hlmarkwn.cxx:200
#define LERR_DOCNOTOPEN
static BitmapEx GetBitmap(const css::uno::Reference< css::awt::XBitmap > &rxBitmap)
void RestoreLastSelection()
Definition: hlmarkwn.cxx:166
#define LERR_NOERROR
sal_uInt16 SetError(sal_uInt16 nError)
Definition: hlmarkwn.cxx:113
OUString CuiResId(TranslateId aKey)
Definition: cuiresmgr.cxx:23
IMPL_LINK_NOARG(SvxHlinkDlgMarkWnd, DoubleClickApplyHdl_Impl, weld::TreeView &, bool)
Definition: hlmarkwn.cxx:465
int i
bool RefreshFromDoc(const OUString &aURL)
Definition: hlmarkwn.cxx:229
void SetUserData(const css::uno::Sequence< css::beans::NamedValue > &lData)
float u
virtual OUString get_text(int row, int col=-1) const =0
virtual void expand_row(const TreeIter &rIter)=0
constexpr OUStringLiteral TG_SETTING_MANAGER
Definition: hlmarkwn.cxx:162
virtual bool iter_children(TreeIter &rIter) const =0
RegionData_Impl * mpParent
std::unique_ptr< weld::Label > mxError
Definition: hlmarkwn.hxx:40
OUString toId(const void *pValue)
virtual void select(int pos)=0
bool SelectEntry(std::u16string_view aStrMark)
Definition: hlmarkwn.cxx:454
sal_uInt16 mnError
Definition: hlmarkwn.hxx:35
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
bool Exists() const
Reference< XExecutableDialog > m_xDialog
Reference< XComponentContext > getProcessComponentContext()
css::uno::Any GetUserItem(const OUString &sName) const
SvxHlinkDlgMarkWnd(weld::Window *pParentDialog, SvxHyperlinkTabPageBase *pParentPage)
Definition: hlmarkwn.cxx:68
std::unique_ptr< weld::Button > mxBtClose
Definition: hlmarkwn.hxx:38
css::uno::Reference< css::graphic::XGraphic > GetXGraphic() const
virtual bool iter_next_sibling(TreeIter &rIter) const =0
constexpr OUStringLiteral TG_SETTING_LASTMARK
Definition: hlmarkwn.cxx:163
Tabpage : Basisclass.
Definition: hltpbase.hxx:46
std::unique_ptr< weld::TreeView > mxLbTree
Definition: hlmarkwn.hxx:39
sal_uInt16 nPos
OUString sId
virtual void scroll_to_row(int row)=0