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 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
176OUString 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
188OUString 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
199sal_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
210OUString 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
221OUString 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>(
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
341AutoTextGroup* 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
372void 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: */
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:221
OUString GetGroupName(size_t nPos)
Definition: gloslst.cxx:176
size_t GetGroupCount()
Definition: gloslst.cxx:169
void ClearGroups()
Definition: gloslst.cxx:437
virtual void Invoke() override
Definition: gloslst.cxx:334
virtual ~SwGlossaryList() override
Definition: gloslst.cxx:96
std::vector< std::unique_ptr< AutoTextGroup > > m_aGroupArr
Definition: gloslst.hxx:49
void Update()
Definition: gloslst.cxx:232
bool m_bFilled
Definition: gloslst.hxx:51
static void FillGroup(AutoTextGroup *pGroup, SwGlossaries *pGloss)
Definition: gloslst.cxx:351
void HasLongName(const std::vector< OUString > &rBeginCandidates, std::vector< std::pair< OUString, sal_uInt16 > > &rLongNames)
Definition: gloslst.cxx:372
OUString GetBlockLongName(size_t nGroup, sal_uInt16 nBlock)
Definition: gloslst.cxx:210
AutoTextGroup * FindGroup(std::u16string_view rGroupName)
Definition: gloslst.cxx:341
OUString GetGroupTitle(size_t nPos)
Definition: gloslst.cxx:188
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:199
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)
#define GLOS_DELIM
Definition: glosdoc.hxx:41
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:786
static std::unique_ptr< SwGlossaries > pGlossaries
Definition: initui.cxx:38
SW_DLLPUBLIC SwGlossaries * GetGlossaries()
Definition: initui.cxx:162
sal_uInt16 nPos
const char * sName
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:116
RET_OK