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