1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <tools/urlobj.hxx>
21 #include <vcl/svapp.hxx>
22 #include <vcl/weld.hxx>
23 #include <unotools/transliterationwrapper.hxx>
24 #include <unotools/tempfile.hxx>
25 #include <unotools/pathoptions.hxx>
26 #include <osl/diagnose.h>
27 #include <o3tl/string_view.hxx>
29 #include <swtypes.hxx>
30 #include <glosbib.hxx>
31 #include <gloshdl.hxx>
32 #include <glossary.hxx>
33 #include <glosdoc.hxx>
34 #include <swunohelper.hxx>
36 #include <strings.hrc>
38 #define PATH_CASE_SENSITIVE 0x01
39 #define PATH_READONLY 0x02
41 #define RENAME_TOKEN_DELIM u'\x0001'
43 SwGlossaryGroupDlg::SwGlossaryGroupDlg(weld::Window
* pParent
,
44 std::vector
<OUString
> const& rPathArr
, SwGlossaryHdl
*pHdl
)
45 : SfxDialogController(pParent
, "modules/swriter/ui/editcategories.ui",
46 "EditCategoriesDialog")
49 , m_xNameED(m_xBuilder
->weld_entry("name"))
50 , m_xPathLB(m_xBuilder
->weld_combo_box("pathlb"))
51 , m_xGroupTLB(m_xBuilder
->weld_tree_view("group"))
52 , m_xNewPB(m_xBuilder
->weld_button("new"))
53 , m_xDelPB(m_xBuilder
->weld_button("delete"))
54 , m_xRenamePB(m_xBuilder
->weld_button("rename"))
56 int nWidth
= m_xGroupTLB
->get_approximate_digit_width() * 34;
57 m_xPathLB
->set_size_request(nWidth
, -1);
58 //just has to be something small, real size will be available space
59 m_xGroupTLB
->set_size_request(nWidth
, m_xGroupTLB
->get_height_rows(10));
61 m_xGroupTLB
->set_column_fixed_widths( { nWidth
} );
62 m_xGroupTLB
->connect_changed(LINK(this, SwGlossaryGroupDlg
, SelectHdl
));
64 m_xNewPB
->connect_clicked(LINK(this, SwGlossaryGroupDlg
, NewHdl
));
65 m_xDelPB
->connect_clicked(LINK(this, SwGlossaryGroupDlg
, DeleteHdl
));
66 m_xNameED
->connect_changed(LINK(this, SwGlossaryGroupDlg
, ModifyHdl
));
67 m_xNameED
->connect_insert_text(LINK(this, SwGlossaryGroupDlg
, EditInsertTextHdl
));
68 m_xPathLB
->connect_changed(LINK(this, SwGlossaryGroupDlg
, ModifyListBoxHdl
));
69 m_xRenamePB
->connect_clicked(LINK(this, SwGlossaryGroupDlg
, RenameHdl
));
71 m_xNameED
->connect_size_allocate(LINK(this, SwGlossaryGroupDlg
, EntrySizeAllocHdl
));
72 m_xPathLB
->connect_size_allocate(LINK(this, SwGlossaryGroupDlg
, EntrySizeAllocHdl
));
74 for (size_t i
= 0; i
< rPathArr
.size(); ++i
)
76 INetURLObject
aTempURL(rPathArr
[i
]);
77 const OUString sPath
= aTempURL
.GetMainURL(INetURLObject::DecodeMechanism::WithCharset
);
78 sal_uInt32 nCaseReadonly
= 0;
79 utl::TempFileNamed
aTempFile(&sPath
);
80 aTempFile
.EnableKillingFile();
81 if(!aTempFile
.IsValid())
82 nCaseReadonly
|= PATH_READONLY
;
83 else if( SWUnoHelper::UCB_IsCaseSensitiveFileName( aTempFile
.GetURL()))
84 nCaseReadonly
|= PATH_CASE_SENSITIVE
;
85 m_xPathLB
->append(OUString::number(nCaseReadonly
), sPath
);
87 m_xPathLB
->set_active(0);
88 m_xPathLB
->set_sensitive(true);
90 const size_t nCount
= pHdl
->GetGroupCnt();
91 /* tdf#111870 "My AutoText" comes from mytexts.bau but should be translated
92 here as well, see also SwGlossaryDlg::Init */
93 static const OUStringLiteral
sMyAutoTextEnglish(u
"My AutoText");
94 for( size_t i
= 0; i
< nCount
; ++i
)
97 OUString sGroup
= pHdl
->GetGroupName(i
, &sTitle
);
100 GlosBibUserData
* pData
= new GlosBibUserData
;
101 pData
->sGroupName
= sGroup
;
102 if ( sTitle
== sMyAutoTextEnglish
)
103 pData
->sGroupTitle
= SwResId(STR_MY_AUTOTEXT
);
105 pData
->sGroupTitle
= sTitle
;
106 pData
->sPath
= m_xPathLB
->get_text(o3tl::toInt32(o3tl::getToken(sGroup
, 1, GLOS_DELIM
)));
107 const OUString
sId(weld::toId(pData
));
108 m_xGroupTLB
->append(sId
, pData
->sGroupTitle
);
109 int nEntry
= m_xGroupTLB
->find_id(sId
);
110 m_xGroupTLB
->set_text(nEntry
, pData
->sPath
, 1);
113 m_xGroupTLB
->make_sorted();
116 SwGlossaryGroupDlg::~SwGlossaryGroupDlg()
118 int nCount
= m_xGroupTLB
->n_children();
119 for (int i
= 0; i
< nCount
; ++i
)
121 GlosBibUserData
* pUserData
= weld::fromId
<GlosBibUserData
*>(m_xGroupTLB
->get_id(i
));
126 short SwGlossaryGroupDlg::run()
128 short nRet
= SfxDialogController::run();
134 void SwGlossaryGroupDlg::Apply()
136 if (m_xNewPB
->get_sensitive())
139 const OUString aActGroup
= SwGlossaryDlg::GetCurrGroup();
141 for (const auto& removedStr
: m_RemovedArr
)
144 const OUString sDelGroup
= removedStr
.getToken(0, '\t', nIdx
);
145 if( sDelGroup
== aActGroup
)
147 //when the current group is deleted, the current group has to be relocated
148 if (m_xGroupTLB
->n_children())
150 GlosBibUserData
* pUserData
= weld::fromId
<GlosBibUserData
*>(m_xGroupTLB
->get_id(0));
151 m_pGlosHdl
->SetCurGroup(pUserData
->sGroupName
);
154 const OUString
sMsg(SwResId(STR_QUERY_DELETE_GROUP1
)
155 + o3tl::getToken(removedStr
, 0, '\t', nIdx
)
156 + SwResId(STR_QUERY_DELETE_GROUP2
));
158 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(m_pParent
,
159 VclMessageType::Question
, VclButtonsType::YesNo
, sMsg
));
160 xQueryBox
->set_default_response(RET_NO
);
161 if (RET_YES
== xQueryBox
->run())
162 m_pGlosHdl
->DelGroup( sDelGroup
);
165 //don't rename before there was one
166 for (auto it(m_RenamedArr
.cbegin()); it
!= m_RenamedArr
.cend(); ++it
)
169 OUString
const sOld(it
->getToken(0, RENAME_TOKEN_DELIM
, nIdx
));
170 OUString
sNew(it
->getToken(0, RENAME_TOKEN_DELIM
, nIdx
));
171 OUString
const sTitle(it
->getToken(0, RENAME_TOKEN_DELIM
, nIdx
));
172 m_pGlosHdl
->RenameGroup(sOld
, sNew
, sTitle
);
173 if (it
== m_RenamedArr
.begin())
175 m_sCreatedGroup
= sNew
;
178 for (auto& sNewGroup
: m_InsertedArr
)
180 OUString sNewTitle
= sNewGroup
.getToken(0, GLOS_DELIM
);
181 if( sNewGroup
!= aActGroup
)
183 m_pGlosHdl
->NewGroup(sNewGroup
, sNewTitle
);
184 if(m_sCreatedGroup
.isEmpty())
185 m_sCreatedGroup
= sNewGroup
;
190 IMPL_LINK_NOARG( SwGlossaryGroupDlg
, SelectHdl
, weld::TreeView
&, void )
192 m_xNewPB
->set_sensitive(false);
193 int nFirstEntry
= m_xGroupTLB
->get_selected_index();
194 if (nFirstEntry
== -1)
197 GlosBibUserData
* pUserData
= weld::fromId
<GlosBibUserData
*>(m_xGroupTLB
->get_id(nFirstEntry
));
198 const OUString
sEntry(pUserData
->sGroupName
);
199 const OUString
sName(m_xNameED
->get_text());
200 bool bExists
= false;
201 int nPos
= m_xGroupTLB
->find_text(sName
);
204 GlosBibUserData
* pFoundData
= weld::fromId
<GlosBibUserData
*>(m_xGroupTLB
->get_id(nPos
));
205 bExists
= pFoundData
->sGroupName
== sEntry
;
208 m_xRenamePB
->set_sensitive(!bExists
&& !sName
.isEmpty());
209 m_xDelPB
->set_sensitive(IsDeleteAllowed(sEntry
));
212 IMPL_LINK_NOARG(SwGlossaryGroupDlg
, NewHdl
, weld::Button
&, void)
214 OUString sGroup
= m_xNameED
->get_text()
215 + OUStringChar(GLOS_DELIM
)
216 + OUString::number(m_xPathLB
->get_active());
217 OSL_ENSURE(!m_pGlosHdl
->FindGroupName(sGroup
), "group already available!");
218 m_InsertedArr
.push_back(sGroup
);
219 GlosBibUserData
* pData
= new GlosBibUserData
;
220 pData
->sPath
= m_xPathLB
->get_active_text();
221 pData
->sGroupName
= sGroup
;
222 pData
->sGroupTitle
= m_xNameED
->get_text();
223 OUString
sId(weld::toId(pData
));
224 m_xGroupTLB
->append(sId
, m_xNameED
->get_text());
225 int nEntry
= m_xGroupTLB
->find_id(sId
);
226 m_xGroupTLB
->set_text(nEntry
, pData
->sPath
, 1);
227 m_xGroupTLB
->select(nEntry
);
228 SelectHdl(*m_xGroupTLB
);
229 m_xGroupTLB
->scroll_to_row(nEntry
);
232 IMPL_LINK_NOARG(SwGlossaryGroupDlg
, EntrySizeAllocHdl
, const Size
&, void)
234 std::vector
<int> aWidths
;
235 int x
, y
, width
, height
;
236 if (m_xPathLB
->get_extents_relative_to(*m_xGroupTLB
, x
, y
, width
, height
))
238 aWidths
.push_back(x
);
239 m_xGroupTLB
->set_column_fixed_widths(aWidths
);
243 IMPL_LINK( SwGlossaryGroupDlg
, DeleteHdl
, weld::Button
&, rButton
, void )
245 int nEntry
= m_xGroupTLB
->get_selected_index();
248 rButton
.set_sensitive(false);
251 GlosBibUserData
* pUserData
= weld::fromId
<GlosBibUserData
*>(m_xGroupTLB
->get_id(nEntry
));
252 OUString
const sEntry(pUserData
->sGroupName
);
253 // if the name to be deleted is among the new ones - get rid of it
255 auto it
= std::find(m_InsertedArr
.begin(), m_InsertedArr
.end(), sEntry
);
256 if (it
!= m_InsertedArr
.end())
258 m_InsertedArr
.erase(it
);
261 // it should probably be renamed?
264 it
= std::find_if(m_RenamedArr
.begin(), m_RenamedArr
.end(),
265 [&sEntry
](OUString
& s
) { return o3tl::getToken(s
, 0, RENAME_TOKEN_DELIM
) == sEntry
; });
266 if (it
!= m_RenamedArr
.end())
268 m_RenamedArr
.erase(it
);
274 m_RemovedArr
.emplace_back(pUserData
->sGroupName
+ "\t" + pUserData
->sGroupTitle
);
277 m_xGroupTLB
->remove(nEntry
);
278 if (!m_xGroupTLB
->n_children())
279 rButton
.set_sensitive(false);
280 //the content must be deleted - otherwise the new handler would be called in Apply()
281 m_xNameED
->set_text(OUString());
282 ModifyHdl(*m_xNameED
);
285 IMPL_LINK_NOARG(SwGlossaryGroupDlg
, RenameHdl
, weld::Button
&, void)
287 int nEntry
= m_xGroupTLB
->get_selected_index();
288 GlosBibUserData
* pUserData
= weld::fromId
<GlosBibUserData
*>(m_xGroupTLB
->get_id(nEntry
));
289 OUString
sEntry(pUserData
->sGroupName
);
291 const OUString
sNewTitle(m_xNameED
->get_text());
292 OUString sNewName
= sNewTitle
293 + OUStringChar(GLOS_DELIM
)
294 + OUString::number(m_xPathLB
->get_active());
295 OSL_ENSURE(!m_pGlosHdl
->FindGroupName(sNewName
), "group already available!");
297 // if the name to be renamed is among the new ones - replace
299 auto it
= std::find(m_InsertedArr
.begin(), m_InsertedArr
.end(), sEntry
);
300 if (it
!= m_InsertedArr
.end())
302 m_InsertedArr
.erase(it
);
303 m_InsertedArr
.push_back(sNewName
);
308 sEntry
+= OUStringChar(RENAME_TOKEN_DELIM
) + sNewName
309 + OUStringChar(RENAME_TOKEN_DELIM
) + sNewTitle
;
310 m_RenamedArr
.push_back(sEntry
);
313 m_xGroupTLB
->remove(nEntry
);
315 GlosBibUserData
* pData
= new GlosBibUserData
;
316 pData
->sPath
= m_xPathLB
->get_active_text();
317 pData
->sGroupName
= sNewName
;
318 pData
->sGroupTitle
= sNewTitle
;
320 OUString
sId(weld::toId(pData
));
321 m_xGroupTLB
->append(sId
, m_xNameED
->get_text());
322 nEntry
= m_xGroupTLB
->find_id(sId
);
323 m_xGroupTLB
->set_text(nEntry
, m_xPathLB
->get_active_text(), 1);
324 m_xGroupTLB
->select(nEntry
);
325 SelectHdl(*m_xGroupTLB
);
326 m_xGroupTLB
->scroll_to_row(nEntry
);
329 IMPL_LINK_NOARG(SwGlossaryGroupDlg
, ModifyListBoxHdl
, weld::ComboBox
&, void)
331 ModifyHdl(*m_xNameED
);
334 IMPL_LINK_NOARG(SwGlossaryGroupDlg
, ModifyHdl
, weld::Entry
&, void)
336 const OUString
sEntry(m_xNameED
->get_text());
337 bool bEnableNew
= true;
338 bool bEnableDel
= false;
339 sal_uInt32 nCaseReadonly
= m_xPathLB
->get_active_id().toUInt32();
340 bool bDirReadonly
= 0 != (nCaseReadonly
&PATH_READONLY
);
342 if (sEntry
.isEmpty() || bDirReadonly
)
344 else if(!sEntry
.isEmpty())
346 int nPos
= m_xGroupTLB
->find_text(sEntry
);
347 //if it's not case sensitive you have to search for yourself
350 const ::utl::TransliterationWrapper
& rSCmp
= GetAppCmpStrIgnore();
351 for (int i
= 0, nEntryCount
= m_xGroupTLB
->n_children(); i
< nEntryCount
; ++i
)
353 const OUString sTemp
= m_xGroupTLB
->get_text(i
, 0);
354 nCaseReadonly
= m_xPathLB
->get_id(m_xPathLB
->find_text(m_xGroupTLB
->get_text(i
,1))).toUInt32();
355 bool bCase
= 0 != (nCaseReadonly
& PATH_CASE_SENSITIVE
);
357 if( !bCase
&& rSCmp
.isEqual( sTemp
, sEntry
))
367 m_xGroupTLB
->select(nPos
);
368 m_xGroupTLB
->scroll_to_row(nPos
);
369 SelectHdl(*m_xGroupTLB
);
372 int nEntry
= m_xGroupTLB
->get_selected_index();
375 GlosBibUserData
* pUserData
= weld::fromId
<GlosBibUserData
*>(m_xGroupTLB
->get_id(nEntry
));
376 bEnableDel
= IsDeleteAllowed(pUserData
->sGroupName
);
379 m_xDelPB
->set_sensitive(bEnableDel
);
380 m_xNewPB
->set_sensitive(bEnableNew
);
381 m_xRenamePB
->set_sensitive(bEnableNew
&& nEntry
!= -1);
384 bool SwGlossaryGroupDlg::IsDeleteAllowed(const OUString
&rGroup
)
386 bool bDel
= !m_pGlosHdl
->IsReadOnly(&rGroup
);
388 // OM: if the name is among the new region name, it is deletable
389 // as well! Because for non existing region names ReadOnly issues
392 auto it
= std::find(m_InsertedArr
.cbegin(), m_InsertedArr
.cend(), rGroup
);
393 if (it
!= m_InsertedArr
.cend())
399 IMPL_STATIC_LINK(SwGlossaryGroupDlg
, EditInsertTextHdl
, OUString
&, rText
, bool)
401 rText
= rText
.replaceAll(OUStringChar(SVT_SEARCHPATH_DELIMITER
), "");
405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */