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