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 <tools/stream.hxx>
22 #include <vcl/help.hxx>
23 #include <vcl/svapp.hxx>
24 #include <vcl/weld.hxx>
25 #include <unotools/transliterationwrapper.hxx>
26 #include <unotools/tempfile.hxx>
27 #include <unotools/pathoptions.hxx>
28 #include <osl/diagnose.h>
30 #include <swtypes.hxx>
31 #include <glosbib.hxx>
32 #include <gloshdl.hxx>
33 #include <actctrl.hxx>
34 #include <glossary.hxx>
35 #include <glosdoc.hxx>
36 #include <swunohelper.hxx>
38 #include <strings.hrc>
40 #define PATH_CASE_SENSITIVE 0x01
41 #define PATH_READONLY 0x02
43 #define RENAME_TOKEN_DELIM u'\x0001'
45 SwGlossaryGroupDlg::SwGlossaryGroupDlg(weld::Window
* pParent
,
46 std::vector
<OUString
> const& rPathArr
, SwGlossaryHdl
*pHdl
)
47 : SfxDialogController(pParent
, "modules/swriter/ui/editcategories.ui",
48 "EditCategoriesDialog")
51 , m_xNameED(m_xBuilder
->weld_entry("name"))
52 , m_xPathLB(m_xBuilder
->weld_combo_box("pathlb"))
53 , m_xGroupTLB(m_xBuilder
->weld_tree_view("group"))
54 , m_xNewPB(m_xBuilder
->weld_button("new"))
55 , m_xDelPB(m_xBuilder
->weld_button("delete"))
56 , m_xRenamePB(m_xBuilder
->weld_button("rename"))
58 int nWidth
= m_xGroupTLB
->get_approximate_digit_width() * 34;
59 m_xPathLB
->set_size_request(nWidth
, -1);
60 //just has to be something small, real size will be available space
61 m_xGroupTLB
->set_size_request(nWidth
, m_xGroupTLB
->get_height_rows(10));
63 std::vector
<int> aWidths
;
64 aWidths
.push_back(nWidth
);
65 m_xGroupTLB
->set_column_fixed_widths(aWidths
);
66 m_xGroupTLB
->connect_changed(LINK(this, SwGlossaryGroupDlg
, SelectHdl
));
68 m_xNewPB
->connect_clicked(LINK(this, SwGlossaryGroupDlg
, NewHdl
));
69 m_xDelPB
->connect_clicked(LINK(this, SwGlossaryGroupDlg
, DeleteHdl
));
70 m_xNameED
->connect_changed(LINK(this, SwGlossaryGroupDlg
, ModifyHdl
));
71 m_xNameED
->connect_insert_text(LINK(this, SwGlossaryGroupDlg
, EditInsertTextHdl
));
72 m_xPathLB
->connect_changed(LINK(this, SwGlossaryGroupDlg
, ModifyListBoxHdl
));
73 m_xRenamePB
->connect_clicked(LINK(this, SwGlossaryGroupDlg
, RenameHdl
));
75 m_xNameED
->connect_size_allocate(LINK(this, SwGlossaryGroupDlg
, EntrySizeAllocHdl
));
76 m_xPathLB
->connect_size_allocate(LINK(this, SwGlossaryGroupDlg
, EntrySizeAllocHdl
));
78 for (size_t i
= 0; i
< rPathArr
.size(); ++i
)
80 INetURLObject
aTempURL(rPathArr
[i
]);
81 const OUString sPath
= aTempURL
.GetMainURL(INetURLObject::DecodeMechanism::WithCharset
);
82 sal_uInt32 nCaseReadonly
= 0;
83 utl::TempFile
aTempFile(&sPath
);
84 aTempFile
.EnableKillingFile();
85 if(!aTempFile
.IsValid())
86 nCaseReadonly
|= PATH_READONLY
;
87 else if( SWUnoHelper::UCB_IsCaseSensitiveFileName( aTempFile
.GetURL()))
88 nCaseReadonly
|= PATH_CASE_SENSITIVE
;
89 m_xPathLB
->append(OUString::number(nCaseReadonly
), sPath
);
91 m_xPathLB
->set_active(0);
92 m_xPathLB
->set_sensitive(true);
94 const size_t nCount
= pHdl
->GetGroupCnt();
95 /* tdf#111870 "My AutoText" comes from mytexts.bau but should be translated
96 here as well, see also SwGlossaryDlg::Init */
97 const OUString
sMyAutoTextEnglish("My AutoText");
98 for( size_t i
= 0; i
< nCount
; ++i
)
101 OUString sGroup
= pHdl
->GetGroupName(i
, &sTitle
);
104 GlosBibUserData
* pData
= new GlosBibUserData
;
105 pData
->sGroupName
= sGroup
;
106 if ( sTitle
== sMyAutoTextEnglish
)
107 pData
->sGroupTitle
= SwResId(STR_MY_AUTOTEXT
);
109 pData
->sGroupTitle
= sTitle
;
110 pData
->sPath
= m_xPathLB
->get_text(sGroup
.getToken(1, GLOS_DELIM
).toInt32());
111 const OUString
sId(OUString::number(reinterpret_cast<sal_Int64
>(pData
)));
112 m_xGroupTLB
->append(sId
, pData
->sGroupTitle
);
113 int nEntry
= m_xGroupTLB
->find_id(sId
);
114 m_xGroupTLB
->set_text(nEntry
, pData
->sPath
, 1);
117 m_xGroupTLB
->make_sorted();
120 SwGlossaryGroupDlg::~SwGlossaryGroupDlg()
122 int nCount
= m_xGroupTLB
->n_children();
123 for (int i
= 0; i
< nCount
; ++i
)
125 GlosBibUserData
* pUserData
= reinterpret_cast<GlosBibUserData
*>(m_xGroupTLB
->get_id(i
).toInt64());
130 short SwGlossaryGroupDlg::run()
132 short nRet
= SfxDialogController::run();
138 void SwGlossaryGroupDlg::Apply()
140 if (m_xNewPB
->get_sensitive())
143 const OUString aActGroup
= SwGlossaryDlg::GetCurrGroup();
145 for (const auto& removedStr
: m_RemovedArr
)
148 const OUString sDelGroup
= removedStr
.getToken(0, '\t', nIdx
);
149 if( sDelGroup
== aActGroup
)
151 //when the current group is deleted, the current group has to be relocated
152 if (m_xGroupTLB
->n_children())
154 GlosBibUserData
* pUserData
= reinterpret_cast<GlosBibUserData
*>(m_xGroupTLB
->get_id(0).toInt64());
155 pGlosHdl
->SetCurGroup(pUserData
->sGroupName
);
158 const OUString
sMsg(SwResId(STR_QUERY_DELETE_GROUP1
)
159 + removedStr
.getToken(0, '\t', nIdx
)
160 + SwResId(STR_QUERY_DELETE_GROUP2
));
162 std::unique_ptr
<weld::MessageDialog
> xQueryBox(Application::CreateMessageDialog(m_pParent
,
163 VclMessageType::Question
, VclButtonsType::YesNo
, sMsg
));
164 xQueryBox
->set_default_response(RET_NO
);
165 if (RET_YES
== xQueryBox
->run())
166 pGlosHdl
->DelGroup( sDelGroup
);
169 //don't rename before there was one
170 for (auto it(m_RenamedArr
.cbegin()); it
!= m_RenamedArr
.cend(); ++it
)
173 OUString
const sOld(it
->getToken(0, RENAME_TOKEN_DELIM
, nIdx
));
174 OUString
sNew(it
->getToken(0, RENAME_TOKEN_DELIM
, nIdx
));
175 OUString
const sTitle(it
->getToken(0, RENAME_TOKEN_DELIM
, nIdx
));
176 pGlosHdl
->RenameGroup(sOld
, sNew
, sTitle
);
177 if (it
== m_RenamedArr
.begin())
179 sCreatedGroup
= sNew
;
182 for (auto& sNewGroup
: m_InsertedArr
)
184 OUString sNewTitle
= sNewGroup
.getToken(0, GLOS_DELIM
);
185 if( sNewGroup
!= aActGroup
)
187 pGlosHdl
->NewGroup(sNewGroup
, sNewTitle
);
188 if(sCreatedGroup
.isEmpty())
189 sCreatedGroup
= sNewGroup
;
194 IMPL_LINK_NOARG( SwGlossaryGroupDlg
, SelectHdl
, weld::TreeView
&, void )
196 m_xNewPB
->set_sensitive(false);
197 int nFirstEntry
= m_xGroupTLB
->get_selected_index();
198 if (nFirstEntry
!= -1)
200 GlosBibUserData
* pUserData
= reinterpret_cast<GlosBibUserData
*>(m_xGroupTLB
->get_id(nFirstEntry
).toInt64());
201 const OUString
sEntry(pUserData
->sGroupName
);
202 const OUString
sName(m_xNameED
->get_text());
203 bool bExists
= false;
204 int nPos
= m_xGroupTLB
->find_text(sName
);
207 GlosBibUserData
* pFoundData
= reinterpret_cast<GlosBibUserData
*>(m_xGroupTLB
->get_id(nPos
).toInt64());
208 fprintf(stderr
, "comparing %s and %s\n",
209 OUStringToOString(pFoundData
->sGroupName
, RTL_TEXTENCODING_UTF8
).getStr(),
210 OUStringToOString(sEntry
, RTL_TEXTENCODING_UTF8
).getStr());
211 bExists
= pFoundData
->sGroupName
== sEntry
;
214 m_xRenamePB
->set_sensitive(!bExists
&& !sName
.isEmpty());
215 fprintf(stderr
, "one rename %d\n", !bExists
&& !sName
.isEmpty());
216 m_xDelPB
->set_sensitive(IsDeleteAllowed(sEntry
));
220 IMPL_LINK_NOARG(SwGlossaryGroupDlg
, NewHdl
, weld::Button
&, void)
222 OUString sGroup
= m_xNameED
->get_text()
223 + OUStringChar(GLOS_DELIM
)
224 + OUString::number(m_xPathLB
->get_active());
225 OSL_ENSURE(!pGlosHdl
->FindGroupName(sGroup
), "group already available!");
226 m_InsertedArr
.push_back(sGroup
);
227 GlosBibUserData
* pData
= new GlosBibUserData
;
228 pData
->sPath
= m_xPathLB
->get_active_text();
229 pData
->sGroupName
= sGroup
;
230 pData
->sGroupTitle
= m_xNameED
->get_text();
231 OUString
sId(OUString::number(reinterpret_cast<sal_Int64
>(pData
)));
232 m_xGroupTLB
->append(sId
, m_xNameED
->get_text());
233 int nEntry
= m_xGroupTLB
->find_id(sId
);
234 m_xGroupTLB
->set_text(nEntry
, pData
->sPath
, 1);
235 m_xGroupTLB
->select(nEntry
);
236 SelectHdl(*m_xGroupTLB
);
237 m_xGroupTLB
->scroll_to_row(nEntry
);
240 IMPL_LINK_NOARG(SwGlossaryGroupDlg
, EntrySizeAllocHdl
, const Size
&, void)
242 std::vector
<int> aWidths
;
243 int x
, y
, width
, height
;
244 if (m_xPathLB
->get_extents_relative_to(*m_xGroupTLB
, x
, y
, width
, height
))
246 aWidths
.push_back(x
);
247 m_xGroupTLB
->set_column_fixed_widths(aWidths
);
251 IMPL_LINK( SwGlossaryGroupDlg
, DeleteHdl
, weld::Button
&, rButton
, void )
253 int nEntry
= m_xGroupTLB
->get_selected_index();
256 rButton
.set_sensitive(false);
259 GlosBibUserData
* pUserData
= reinterpret_cast<GlosBibUserData
*>(m_xGroupTLB
->get_id(nEntry
).toInt64());
260 OUString
const sEntry(pUserData
->sGroupName
);
261 // if the name to be deleted is among the new ones - get rid of it
263 auto it
= std::find(m_InsertedArr
.begin(), m_InsertedArr
.end(), sEntry
);
264 if (it
!= m_InsertedArr
.end())
266 m_InsertedArr
.erase(it
);
269 // it should probably be renamed?
272 it
= std::find_if(m_RenamedArr
.begin(), m_RenamedArr
.end(),
273 [&sEntry
](OUString
& s
) { return s
.getToken(0, RENAME_TOKEN_DELIM
) == sEntry
; });
274 if (it
!= m_RenamedArr
.end())
276 m_RenamedArr
.erase(it
);
282 m_RemovedArr
.emplace_back(pUserData
->sGroupName
+ "\t" + pUserData
->sGroupTitle
);
285 m_xGroupTLB
->remove(nEntry
);
286 if (!m_xGroupTLB
->n_children())
287 rButton
.set_sensitive(false);
288 //the content must be deleted - otherwise the new handler would be called in Apply()
289 m_xNameED
->set_text(OUString());
290 ModifyHdl(*m_xNameED
);
293 IMPL_LINK_NOARG(SwGlossaryGroupDlg
, RenameHdl
, weld::Button
&, void)
295 int nEntry
= m_xGroupTLB
->get_selected_index();
296 GlosBibUserData
* pUserData
= reinterpret_cast<GlosBibUserData
*>(m_xGroupTLB
->get_id(nEntry
).toInt64());
297 OUString
sEntry(pUserData
->sGroupName
);
299 const OUString
sNewTitle(m_xNameED
->get_text());
300 OUString sNewName
= sNewTitle
301 + OUStringChar(GLOS_DELIM
)
302 + OUString::number(m_xPathLB
->get_active());
303 OSL_ENSURE(!pGlosHdl
->FindGroupName(sNewName
), "group already available!");
305 // if the name to be renamed is among the new ones - replace
307 auto it
= std::find(m_InsertedArr
.begin(), m_InsertedArr
.end(), sEntry
);
308 if (it
!= m_InsertedArr
.end())
310 m_InsertedArr
.erase(it
);
311 m_InsertedArr
.push_back(sNewName
);
316 sEntry
+= OUStringChar(RENAME_TOKEN_DELIM
) + sNewName
317 + OUStringChar(RENAME_TOKEN_DELIM
) + sNewTitle
;
318 m_RenamedArr
.push_back(sEntry
);
321 m_xGroupTLB
->remove(nEntry
);
323 GlosBibUserData
* pData
= new GlosBibUserData
;
324 pData
->sPath
= m_xPathLB
->get_active_text();
325 pData
->sGroupName
= sNewName
;
326 pData
->sGroupTitle
= sNewTitle
;
328 OUString
sId(OUString::number(reinterpret_cast<sal_Int64
>(pData
)));
329 m_xGroupTLB
->append(sId
, m_xNameED
->get_text());
330 nEntry
= m_xGroupTLB
->find_id(sId
);
331 m_xGroupTLB
->set_text(nEntry
, m_xPathLB
->get_active_text(), 1);
332 m_xGroupTLB
->select(nEntry
);
333 SelectHdl(*m_xGroupTLB
);
334 m_xGroupTLB
->scroll_to_row(nEntry
);
337 IMPL_LINK_NOARG(SwGlossaryGroupDlg
, ModifyListBoxHdl
, weld::ComboBox
&, void)
339 ModifyHdl(*m_xNameED
);
342 IMPL_LINK_NOARG(SwGlossaryGroupDlg
, ModifyHdl
, weld::Entry
&, void)
344 const OUString
sEntry(m_xNameED
->get_text());
345 bool bEnableNew
= true;
346 bool bEnableDel
= false;
347 sal_uInt32 nCaseReadonly
= m_xPathLB
->get_active_id().toUInt32();
348 bool bDirReadonly
= 0 != (nCaseReadonly
&PATH_READONLY
);
350 if (sEntry
.isEmpty() || bDirReadonly
)
352 else if(!sEntry
.isEmpty())
354 int nPos
= m_xGroupTLB
->find_text(sEntry
);
355 //if it's not case sensitive you have to search for yourself
358 const ::utl::TransliterationWrapper
& rSCmp
= GetAppCmpStrIgnore();
359 for (int i
= 0, nEntryCount
= m_xGroupTLB
->n_children(); i
< nEntryCount
; ++i
)
361 const OUString sTemp
= m_xGroupTLB
->get_text(i
, 0);
362 nCaseReadonly
= m_xPathLB
->get_id(m_xPathLB
->find_text(m_xGroupTLB
->get_text(i
,1))).toUInt32();
363 bool bCase
= 0 != (nCaseReadonly
& PATH_CASE_SENSITIVE
);
365 if( !bCase
&& rSCmp
.isEqual( sTemp
, sEntry
))
375 m_xGroupTLB
->select(nPos
);
376 m_xGroupTLB
->scroll_to_row(nPos
);
377 SelectHdl(*m_xGroupTLB
);
380 int nEntry
= m_xGroupTLB
->get_selected_index();
383 GlosBibUserData
* pUserData
= reinterpret_cast<GlosBibUserData
*>(m_xGroupTLB
->get_id(nEntry
).toInt64());
384 bEnableDel
= IsDeleteAllowed(pUserData
->sGroupName
);
387 m_xDelPB
->set_sensitive(bEnableDel
);
388 m_xNewPB
->set_sensitive(bEnableNew
);
389 m_xRenamePB
->set_sensitive(bEnableNew
&& nEntry
!= -1);
390 fprintf(stderr
, "two rename %d\n", bEnableNew
&& nEntry
!= -1);
393 bool SwGlossaryGroupDlg::IsDeleteAllowed(const OUString
&rGroup
)
395 bool bDel
= !pGlosHdl
->IsReadOnly(&rGroup
);
397 // OM: if the name is among the new region name, it is deletable
398 // as well! Because for non existing region names ReadOnly issues
401 auto it
= std::find(m_InsertedArr
.cbegin(), m_InsertedArr
.cend(), rGroup
);
402 if (it
!= m_InsertedArr
.cend())
408 IMPL_STATIC_LINK(SwGlossaryGroupDlg
, EditInsertTextHdl
, OUString
&, rText
, bool)
410 rText
= rText
.replaceAll(OUStringChar(SVT_SEARCHPATH_DELIMITER
), "");
414 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */