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