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