LibreOffice Module sw (master)  1
gloslst.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 <vcl/weld.hxx>
21 #include <svl/fstathelper.hxx>
22 #include <unotools/pathoptions.hxx>
24 #include <swtypes.hxx>
25 #include <swmodule.hxx>
26 #include <shellio.hxx>
27 #include <initui.hxx>
28 #include <glosdoc.hxx>
29 #include <gloslst.hxx>
30 #include <swunohelper.hxx>
31 #include <view.hxx>
32 
33 #include <vector>
34 
35 #define STRING_DELIM char(0x0A)
36 #define GLOS_TIMEOUT 30000 // update every 30 seconds
37 #define FIND_MAX_GLOS 20
38 
39 namespace {
40 
41 struct TripleString
42 {
43  OUString sGroup;
44  OUString sBlock;
45  OUString sShort;
46 };
47 
48 class SwGlossDecideDlg : public weld::GenericDialogController
49 {
50  std::unique_ptr<weld::Button> m_xOk;
51  std::unique_ptr<weld::TreeView> m_xListLB;
52 
53  DECL_LINK(DoubleClickHdl, weld::TreeView&, bool);
54  DECL_LINK(SelectHdl, weld::TreeView&, void);
55 
56 public:
57  explicit SwGlossDecideDlg(weld::Window* pParent);
58 
59  weld::TreeView& GetTreeView() {return *m_xListLB;}
60 };
61 
62 }
63 
64 SwGlossDecideDlg::SwGlossDecideDlg(weld::Window* pParent)
65  : GenericDialogController(pParent, "modules/swriter/ui/selectautotextdialog.ui", "SelectAutoTextDialog")
66  , m_xOk(m_xBuilder->weld_button("ok"))
67  , m_xListLB(m_xBuilder->weld_tree_view("treeview"))
68 {
69  m_xListLB->set_size_request(m_xListLB->get_approximate_digit_width() * 32,
70  m_xListLB->get_height_rows(8));
71  m_xListLB->connect_row_activated(LINK(this, SwGlossDecideDlg, DoubleClickHdl));
72  m_xListLB->connect_changed(LINK(this, SwGlossDecideDlg, SelectHdl));
73 }
74 
75 IMPL_LINK_NOARG(SwGlossDecideDlg, DoubleClickHdl, weld::TreeView&, bool)
76 {
77  m_xDialog->response(RET_OK);
78  return true;
79 }
80 
81 IMPL_LINK_NOARG(SwGlossDecideDlg, SelectHdl, weld::TreeView&, void)
82 {
83  m_xOk->set_sensitive(m_xListLB->get_selected_index() != -1);
84 }
85 
87  bFilled(false)
88 {
89  SvtPathOptions aPathOpt;
90  sPath = aPathOpt.GetAutoTextPath();
92 }
93 
95 {
96  ClearGroups();
97 }
98 
99 // If the GroupName is already known, then only rShortName
100 // will be filled. Otherwise also rGroupName will be set and
101 // on demand asked for the right group.
102 
103 bool SwGlossaryList::GetShortName(const OUString& rLongName,
104  OUString& rShortName, OUString& rGroupName )
105 {
106  if(!bFilled)
107  Update();
108 
109  std::vector<TripleString> aTripleStrings;
110 
111  size_t nCount = aGroupArr.size();
112  for(size_t i = 0; i < nCount; i++ )
113  {
114  AutoTextGroup* pGroup = aGroupArr[i].get();
115  if(!rGroupName.isEmpty() && rGroupName != pGroup->sName)
116  continue;
117 
118  sal_Int32 nPosLong = 0;
119  for(sal_uInt16 j = 0; j < pGroup->nCount; j++)
120  {
121  const OUString sLong = pGroup->sLongNames.getToken(0, STRING_DELIM, nPosLong);
122  if(rLongName != sLong)
123  continue;
124 
125  TripleString aTriple;
126  aTriple.sGroup = pGroup->sName;
127  aTriple.sBlock = sLong;
128  aTriple.sShort = pGroup->sShortNames.getToken(j, STRING_DELIM);
129  aTripleStrings.push_back(aTriple);
130  }
131  }
132 
133  bool bRet = false;
134  nCount = aTripleStrings.size();
135  if(1 == nCount)
136  {
137  const TripleString& rTriple(aTripleStrings.front());
138  rShortName = rTriple.sShort;
139  rGroupName = rTriple.sGroup;
140  bRet = true;
141  }
142  else if(1 < nCount)
143  {
144  SwView *pView = ::GetActiveView();
145  SwGlossDecideDlg aDlg(pView ? pView->GetFrameWeld() : nullptr);
146  OUString sTitle = aDlg.get_title() + " " + aTripleStrings.front().sBlock;
147  aDlg.set_title(sTitle);
148 
149  weld::TreeView& rLB = aDlg.GetTreeView();
150  for (const auto& rTriple : aTripleStrings)
151  rLB.append_text(rTriple.sGroup.getToken(0, GLOS_DELIM));
152 
153  rLB.select(0);
154  if (aDlg.run() == RET_OK && rLB.get_selected_index() != -1)
155  {
156  const TripleString& rTriple(aTripleStrings[rLB.get_selected_index()]);
157  rShortName = rTriple.sShort;
158  rGroupName = rTriple.sGroup;
159  bRet = true;
160  }
161  else
162  bRet = false;
163  }
164  return bRet;
165 }
166 
168 {
169  if(!bFilled)
170  Update();
171  return aGroupArr.size();
172 }
173 
174 OUString SwGlossaryList::GetGroupName(size_t nPos)
175 {
176  OSL_ENSURE(aGroupArr.size() > nPos, "group not available");
177  if(nPos < aGroupArr.size())
178  {
179  AutoTextGroup* pGroup = aGroupArr[nPos].get();
180  OUString sRet = pGroup->sName;
181  return sRet;
182  }
183  return OUString();
184 }
185 
186 OUString SwGlossaryList::GetGroupTitle(size_t nPos)
187 {
188  OSL_ENSURE(aGroupArr.size() > nPos, "group not available");
189  if(nPos < aGroupArr.size())
190  {
191  AutoTextGroup* pGroup = aGroupArr[nPos].get();
192  return pGroup->sTitle;
193  }
194  return OUString();
195 }
196 
197 sal_uInt16 SwGlossaryList::GetBlockCount(size_t nGroup)
198 {
199  OSL_ENSURE(aGroupArr.size() > nGroup, "group not available");
200  if(nGroup < aGroupArr.size())
201  {
202  AutoTextGroup* pGroup = aGroupArr[nGroup].get();
203  return pGroup->nCount;
204  }
205  return 0;
206 }
207 
208 OUString SwGlossaryList::GetBlockLongName(size_t nGroup, sal_uInt16 nBlock)
209 {
210  OSL_ENSURE(aGroupArr.size() > nGroup, "group not available");
211  if(nGroup < aGroupArr.size())
212  {
213  AutoTextGroup* pGroup = aGroupArr[nGroup].get();
214  return pGroup->sLongNames.getToken(nBlock, STRING_DELIM);
215  }
216  return OUString();
217 }
218 
219 OUString SwGlossaryList::GetBlockShortName(size_t nGroup, sal_uInt16 nBlock)
220 {
221  OSL_ENSURE(aGroupArr.size() > nGroup, "group not available");
222  if(nGroup < aGroupArr.size())
223  {
224  AutoTextGroup* pGroup = aGroupArr[nGroup].get();
225  return pGroup->sShortNames.getToken(nBlock, STRING_DELIM);
226  }
227  return OUString();
228 }
229 
231 {
232  if(!IsActive())
233  Start();
234 
235  SvtPathOptions aPathOpt;
236  const OUString& sTemp( aPathOpt.GetAutoTextPath() );
237  if(sTemp != sPath)
238  {
239  sPath = sTemp;
240  bFilled = false;
241  ClearGroups();
242  }
244  const std::vector<OUString> & rPathArr = pGlossaries->GetPathArray();
245  const OUString sExt( SwGlossaries::GetExtension() );
246  if(!bFilled)
247  {
248  const size_t nGroupCount = pGlossaries->GetGroupCnt();
249  for(size_t i = 0; i < nGroupCount; ++i)
250  {
251  OUString sGrpName = pGlossaries->GetGroupName(i);
252  const size_t nPath = static_cast<size_t>(
253  sGrpName.getToken(1, GLOS_DELIM).toInt32());
254  if( nPath < rPathArr.size() )
255  {
256  std::unique_ptr<AutoTextGroup> pGroup(new AutoTextGroup);
257  pGroup->sName = sGrpName;
258 
259  FillGroup(pGroup.get(), pGlossaries);
260  OUString sName = rPathArr[nPath] + "/" +
261  pGroup->sName.getToken(0, GLOS_DELIM) + sExt;
263  &pGroup->aDateModified,
264  &pGroup->aDateModified );
265 
266  aGroupArr.insert( aGroupArr.begin(), std::move(pGroup) );
267  }
268  }
269  bFilled = true;
270  }
271  else
272  {
273  for( size_t nPath = 0; nPath < rPathArr.size(); nPath++ )
274  {
275  std::vector<OUString> aFoundGroupNames;
276  std::vector<OUString> aFiles;
277  std::vector<DateTime> aDateTimeArr;
278 
279  SWUnoHelper::UCB_GetFileListOfFolder( rPathArr[nPath], aFiles,
280  &sExt, &aDateTimeArr );
281  for( size_t nFiles = 0; nFiles < aFiles.size(); ++nFiles )
282  {
283  const OUString aTitle = aFiles[ nFiles ];
284  ::DateTime& rDT = aDateTimeArr[ nFiles ];
285 
286  OUString sName( aTitle.copy( 0, aTitle.getLength() - sExt.getLength() ));
287 
288  aFoundGroupNames.push_back(sName);
289  sName += OUStringChar(GLOS_DELIM) + OUString::number( static_cast<sal_uInt16>(nPath) );
290  AutoTextGroup* pFound = FindGroup( sName );
291  if( !pFound )
292  {
293  pFound = new AutoTextGroup;
294  pFound->sName = sName;
295  FillGroup( pFound, pGlossaries );
296  pFound->aDateModified = rDT;
297 
298  aGroupArr.push_back(std::unique_ptr<AutoTextGroup>(pFound));
299  }
300  else if( pFound->aDateModified < rDT )
301  {
302  FillGroup(pFound, pGlossaries);
303  pFound->aDateModified = rDT;
304  }
305  }
306 
307  for( size_t i = aGroupArr.size(); i>0; )
308  {
309  --i;
310  // maybe remove deleted groups
311  AutoTextGroup* pGroup = aGroupArr[i].get();
312  const size_t nGroupPath = static_cast<size_t>(
313  pGroup->sName.getToken( 1, GLOS_DELIM).toInt32());
314  // Only the groups will be checked which are registered
315  // for the current subpath.
316  if( nGroupPath == nPath )
317  {
318  OUString sCompareGroup = pGroup->sName.getToken(0, GLOS_DELIM);
319  bool bFound = std::any_of(aFoundGroupNames.begin(), aFoundGroupNames.end(),
320  [&sCompareGroup](const OUString& rGroupName) { return sCompareGroup == rGroupName; });
321 
322  if(!bFound)
323  {
324  aGroupArr.erase(aGroupArr.begin() + i);
325  }
326  }
327  }
328  }
329  }
330 }
331 
333 {
334  // Only update automatically if a SwView has the focus.
335  if(::GetActiveView())
336  Update();
337 }
338 
339 AutoTextGroup* SwGlossaryList::FindGroup(const OUString& rGroupName)
340 {
341  for(const auto & pRet : aGroupArr)
342  {
343  if(pRet->sName == rGroupName)
344  return pRet.get();
345  }
346  return nullptr;
347 }
348 
350 {
351  std::unique_ptr<SwTextBlocks> pBlock = pGlossaries->GetGroupDoc(pGroup->sName);
352  pGroup->nCount = pBlock ? pBlock->GetCount() : 0;
353  pGroup->sLongNames.clear();
354  pGroup->sShortNames.clear();
355  if(pBlock)
356  pGroup->sTitle = pBlock->GetName();
357 
358  for(sal_uInt16 j = 0; j < pGroup->nCount; j++)
359  {
360  pGroup->sLongNames += pBlock->GetLongName(j)
361  + OUStringChar(STRING_DELIM);
362  pGroup->sShortNames += pBlock->GetShortName(j)
363  + OUStringChar(STRING_DELIM);
364  }
365 }
366 
367 // Give back all (not exceeding FIND_MAX_GLOS) found modules
368 // with matching beginning.
369 
370 void SwGlossaryList::HasLongName(const std::vector<OUString>& rBeginCandidates,
371  std::vector<std::pair<OUString, sal_uInt16>>& rLongNames)
372 {
373  if(!bFilled)
374  Update();
375  const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
376  // We store results for all candidate words in separate lists, so that later
377  // we can sort them according to the candidate position
378  std::vector<std::vector<OUString>> aResults(rBeginCandidates.size());
379 
380  // We can't break after FIND_MAX_GLOS items found, since first items may have ended up in
381  // lower-priority lists, and those from higher-priority lists are yet to come. So process all.
382  for (const auto& pGroup : aGroupArr)
383  {
384  sal_Int32 nIdx{ 0 };
385  for(sal_uInt16 j = 0; j < pGroup->nCount; j++)
386  {
387  OUString sBlock = pGroup->sLongNames.getToken(0, STRING_DELIM, nIdx);
388  for (size_t i = 0; i < rBeginCandidates.size(); ++i)
389  {
390  const OUString& s = rBeginCandidates[i];
391  if (s.getLength() + 1 < sBlock.getLength()
392  && rSCmp.isEqual(sBlock.copy(0, s.getLength()), s))
393  {
394  aResults[i].push_back(sBlock);
395  }
396  }
397  }
398  }
399 
400  std::vector<std::pair<OUString, sal_uInt16>> aAllResults;
401  // Sort and concatenate all result lists. See QuickHelpData::SortAndFilter
402  for (size_t i = 0; i < rBeginCandidates.size(); ++i)
403  {
404  std::sort(aResults[i].begin(), aResults[i].end(),
405  [origWord = rBeginCandidates[i]](const OUString& s1, const OUString& s2) {
406  int nRet = s1.compareToIgnoreAsciiCase(s2);
407  if (nRet == 0)
408  {
409  // fdo#61251 sort stuff that starts with the exact rOrigWord before
410  // another ignore-case candidate
411  int n1StartsWithOrig = s1.startsWith(origWord) ? 0 : 1;
412  int n2StartsWithOrig = s2.startsWith(origWord) ? 0 : 1;
413  return n1StartsWithOrig < n2StartsWithOrig;
414  }
415  return nRet < 0;
416  });
417  // All suggestions must be accompanied with length of the text they would replace
418  std::transform(aResults[i].begin(), aResults[i].end(), std::back_inserter(aAllResults),
419  [nLen = sal_uInt16(rBeginCandidates[i].getLength())](const OUString& s) {
420  return std::make_pair(s, nLen);
421  });
422  }
423 
424  const auto& it = std::unique(
425  aAllResults.begin(), aAllResults.end(),
426  [](const std::pair<OUString, sal_uInt16>& s1, const std::pair<OUString, sal_uInt16>& s2) {
427  return s1.first.equalsIgnoreAsciiCase(s2.first);
428  });
429  if (const auto nCount = std::min<size_t>(std::distance(aAllResults.begin(), it), FIND_MAX_GLOS))
430  {
431  rLongNames.insert(rLongNames.end(), aAllResults.begin(), aAllResults.begin() + nCount);
432  }
433 }
434 
436 {
437  aGroupArr.clear();
438  bFilled = false;
439 }
440 
441 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString sLongNames
Definition: gloslst.hxx:36
const ::utl::TransliterationWrapper & GetAppCmpStrIgnore()
Definition: init.cxx:792
sal_uInt16 GetBlockCount(size_t nGroup)
Definition: gloslst.cxx:197
OUString sShortNames
Definition: gloslst.hxx:37
DateTime aDateModified
Definition: gloslst.hxx:38
SwView * GetActiveView()
Definition: swmodul1.cxx:115
void Update()
Definition: gloslst.cxx:230
size_t GetGroupCnt()
Definition: glosdoc.cxx:97
size_t GetGroupCount()
Definition: gloslst.cxx:167
std::unique_ptr< SwTextBlocks > GetGroupDoc(const OUString &rName, bool bCreate=false)
Definition: glosdoc.cxx:157
static OUString GetExtension()
Definition: glosdoc.cxx:418
#define FIND_MAX_GLOS
Definition: gloslst.cxx:37
bool IsActive() const
OUString sName
Definition: gloslst.hxx:34
void append_text(const OUString &rStr)
enumrange< T >::Iterator begin(enumrange< T >)
int nCount
OUString GetBlockLongName(size_t nGroup, sal_uInt16 nBlock)
Definition: gloslst.cxx:208
OUString GetGroupName(size_t nPos)
Definition: gloslst.cxx:174
SW_DLLPUBLIC SwGlossaries * GetGlossaries()
Definition: initui.cxx:160
OUString GetBlockShortName(size_t nGroup, sal_uInt16 nBlock)
Definition: gloslst.cxx:219
bool GetShortName(const OUString &rLongName, OUString &rShortName, OUString &rGroupName)
Definition: gloslst.cxx:103
const char * sName
int i
OUString GetGroupTitle(size_t nPos)
Definition: gloslst.cxx:186
virtual int get_selected_index() const =0
static std::unique_ptr< SwGlossaries > pGlossaries
Definition: initui.cxx:36
virtual void Start() override
std::vector< std::unique_ptr< AutoTextGroup > > aGroupArr
Definition: gloslst.hxx:49
virtual void Invoke() override
Definition: gloslst.cxx:332
void SetTimeout(sal_uInt64 nTimeoutMs)
enumrange< T >::Iterator end(enumrange< T >)
static void FillGroup(AutoTextGroup *pGroup, SwGlossaries *pGloss)
Definition: gloslst.cxx:349
OUString sTitle
Definition: gloslst.hxx:35
void HasLongName(const std::vector< OUString > &rBeginCandidates, std::vector< std::pair< OUString, sal_uInt16 >> &rLongNames)
Definition: gloslst.cxx:370
virtual void select(int pos)=0
OUString sPath
Definition: gloslst.hxx:50
OUString const & GetGroupName(size_t)
Definition: glosdoc.cxx:135
weld::Window * GetFrameWeld() const
std::vector< OUString > const & GetPathArray() const
Definition: glosdoc.hxx:120
void ClearGroups()
Definition: gloslst.cxx:435
RET_OK
IMPL_LINK_NOARG(SwGlossDecideDlg, DoubleClickHdl, weld::TreeView &, bool)
Definition: gloslst.cxx:75
bool UCB_GetFileListOfFolder(const OUString &rURL, std::vector< OUString > &rList, const OUString *pExtension, std::vector< ::DateTime > *pDateTimeList)
Reference< XExecutableDialog > m_xDialog
sal_uInt16 nCount
Definition: gloslst.hxx:33
SVL_DLLPUBLIC bool GetModifiedDateTimeOfFile(const OUString &rURL, Date *pDate, tools::Time *pTime)
const OUString & GetAutoTextPath() const
#define GLOS_TIMEOUT
Definition: gloslst.cxx:36
double getLength(const B2DPolygon &rCandidate)
#define GLOS_DELIM
Definition: glosdoc.hxx:41
virtual ~SwGlossaryList() override
Definition: gloslst.cxx:94
#define STRING_DELIM
Definition: gloslst.cxx:35
AutoTextGroup * FindGroup(const OUString &rGroupName)
Definition: gloslst.cxx:339
Definition: view.hxx:146