/src/libreoffice/sfx2/source/doc/autoredactdialog.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ |
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 | | |
10 | | #include <autoredactdialog.hxx> |
11 | | |
12 | | #include <sfx2/filedlghelper.hxx> |
13 | | #include <sfx2/sfxresid.hxx> |
14 | | #include <sfx2/strings.hrc> |
15 | | #include <o3tl/deleter.hxx> |
16 | | #include <osl/file.hxx> |
17 | | #include <sal/log.hxx> |
18 | | #include <vcl/svapp.hxx> |
19 | | #include <vcl/weld/weld.hxx> |
20 | | #include <unotools/viewoptions.hxx> |
21 | | #include <o3tl/string_view.hxx> |
22 | | |
23 | | #include <com/sun/star/ui/dialogs/TemplateDescription.hpp> |
24 | | |
25 | | #include <boost/property_tree/json_parser.hpp> |
26 | | |
27 | | constexpr OUStringLiteral FILEDIALOG_FILTER_JSON = u"*.json"; |
28 | | |
29 | | int TargetsTable::GetRowByTargetName(std::u16string_view sName) |
30 | 0 | { |
31 | 0 | for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i) |
32 | 0 | { |
33 | 0 | RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_xControl->get_id(i)); |
34 | 0 | if (pTarget->sName == sName) |
35 | 0 | { |
36 | 0 | return i; |
37 | 0 | } |
38 | 0 | } |
39 | 0 | return -1; |
40 | 0 | } |
41 | | |
42 | | bool TargetsTable::HasTargetType(RedactionTargetType aTargetType) |
43 | 0 | { |
44 | 0 | for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i) |
45 | 0 | { |
46 | 0 | RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_xControl->get_id(i)); |
47 | 0 | if (pTarget->sType == aTargetType) |
48 | 0 | { |
49 | 0 | return true; |
50 | 0 | } |
51 | 0 | } |
52 | 0 | return false; |
53 | 0 | } |
54 | | |
55 | | TargetsTable::TargetsTable(std::unique_ptr<weld::TreeView> xControl) |
56 | 0 | : m_xControl(std::move(xControl)) |
57 | 0 | { |
58 | 0 | m_xControl->set_size_request(555, 250); |
59 | 0 | std::vector<int> aWidths{ 100, 50, 200, 105, 105 }; |
60 | 0 | m_xControl->set_column_fixed_widths(aWidths); |
61 | 0 | m_xControl->set_selection_mode(SelectionMode::Multiple); |
62 | 0 | } |
63 | | |
64 | | namespace |
65 | | { |
66 | | OUString getTypeName(RedactionTargetType nType) |
67 | 0 | { |
68 | 0 | OUString sTypeName(SfxResId(STR_REDACTION_TARGET_TYPE_UNKNOWN)); |
69 | |
|
70 | 0 | switch (nType) |
71 | 0 | { |
72 | 0 | case RedactionTargetType::REDACTION_TARGET_TEXT: |
73 | 0 | sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_TEXT); |
74 | 0 | break; |
75 | 0 | case RedactionTargetType::REDACTION_TARGET_REGEX: |
76 | 0 | sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_REGEX); |
77 | 0 | break; |
78 | 0 | case RedactionTargetType::REDACTION_TARGET_PREDEFINED: |
79 | 0 | sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_PREDEF); |
80 | 0 | break; |
81 | 0 | case RedactionTargetType::REDACTION_TARGET_IMAGE: |
82 | 0 | sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_IMAGE); |
83 | 0 | break; |
84 | 0 | case RedactionTargetType::REDACTION_TARGET_UNKNOWN: |
85 | 0 | sTypeName = SfxResId(STR_REDACTION_TARGET_TYPE_UNKNOWN); |
86 | 0 | break; |
87 | 0 | } |
88 | | |
89 | 0 | return sTypeName; |
90 | 0 | } |
91 | | |
92 | | /// Returns TypeID to be used in the add/edit target dialog |
93 | | OUString getTypeID(RedactionTargetType nType) |
94 | 0 | { |
95 | 0 | OUString sTypeID(u"unknown"_ustr); |
96 | |
|
97 | 0 | switch (nType) |
98 | 0 | { |
99 | 0 | case RedactionTargetType::REDACTION_TARGET_TEXT: |
100 | 0 | sTypeID = "text"; |
101 | 0 | break; |
102 | 0 | case RedactionTargetType::REDACTION_TARGET_REGEX: |
103 | 0 | sTypeID = "regex"; |
104 | 0 | break; |
105 | 0 | case RedactionTargetType::REDACTION_TARGET_PREDEFINED: |
106 | 0 | sTypeID = "predefined"; |
107 | 0 | break; |
108 | 0 | case RedactionTargetType::REDACTION_TARGET_IMAGE: |
109 | 0 | sTypeID = "image"; |
110 | 0 | break; |
111 | 0 | case RedactionTargetType::REDACTION_TARGET_UNKNOWN: |
112 | 0 | sTypeID = "unknown"; |
113 | 0 | break; |
114 | 0 | } |
115 | | |
116 | 0 | return sTypeID; |
117 | 0 | } |
118 | | } |
119 | | |
120 | | void TargetsTable::InsertTarget(RedactionTarget* pTarget) |
121 | 0 | { |
122 | 0 | if (!pTarget) |
123 | 0 | { |
124 | 0 | SAL_WARN("sfx.doc", "pTarget is null in TargetsTable::InsertTarget()"); |
125 | 0 | return; |
126 | 0 | } |
127 | | |
128 | | // Check if the name is empty or invalid (clashing with another entry's name) |
129 | 0 | if (pTarget->sName.isEmpty() || GetRowByTargetName(pTarget->sName) != -1) |
130 | 0 | { |
131 | 0 | pTarget->sName = GetNameProposal(); |
132 | 0 | } |
133 | |
|
134 | 0 | OUString sContent = pTarget->sContent; |
135 | |
|
136 | 0 | if (pTarget->sType == RedactionTargetType::REDACTION_TARGET_PREDEFINED) |
137 | 0 | { |
138 | | //selection_num;selection_name |
139 | 0 | sContent = sContent.getToken(1, ';'); |
140 | 0 | } |
141 | | |
142 | | // Add to the end |
143 | 0 | int nRow = m_xControl->n_children(); |
144 | 0 | m_xControl->append(weld::toId(pTarget), pTarget->sName); |
145 | 0 | m_xControl->set_text(nRow, getTypeName(pTarget->sType), 1); |
146 | 0 | m_xControl->set_text(nRow, sContent, 2); |
147 | 0 | m_xControl->set_text( |
148 | 0 | nRow, pTarget->bCaseSensitive ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO), |
149 | 0 | 3); |
150 | 0 | m_xControl->set_text( |
151 | 0 | nRow, pTarget->bWholeWords ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO), 4); |
152 | 0 | } |
153 | | |
154 | | RedactionTarget* TargetsTable::GetTargetByName(std::u16string_view sName) |
155 | 0 | { |
156 | 0 | int nEntry = GetRowByTargetName(sName); |
157 | 0 | if (nEntry == -1) |
158 | 0 | return nullptr; |
159 | | |
160 | 0 | return weld::fromId<RedactionTarget*>(m_xControl->get_id(nEntry)); |
161 | 0 | } |
162 | | |
163 | | OUString TargetsTable::GetNameProposal() const |
164 | 0 | { |
165 | 0 | OUString sDefaultTargetName(SfxResId(STR_REDACTION_TARGET)); |
166 | 0 | sal_Int32 nHighestTargetId = 0; |
167 | 0 | for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i) |
168 | 0 | { |
169 | 0 | RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_xControl->get_id(i)); |
170 | 0 | const OUString& sName = pTarget->sName; |
171 | 0 | sal_Int32 nIndex = 0; |
172 | 0 | if (o3tl::getToken(sName, 0, ' ', nIndex) == sDefaultTargetName) |
173 | 0 | { |
174 | 0 | sal_Int32 nCurrTargetId = o3tl::toInt32(o3tl::getToken(sName, 0, ' ', nIndex)); |
175 | 0 | nHighestTargetId = std::max<sal_Int32>(nHighestTargetId, nCurrTargetId); |
176 | 0 | } |
177 | 0 | } |
178 | 0 | return sDefaultTargetName + " " + OUString::number(nHighestTargetId + 1); |
179 | 0 | } |
180 | | |
181 | | void TargetsTable::setRowData(int nRowIndex, const RedactionTarget* pTarget) |
182 | 0 | { |
183 | 0 | OUString sContent = pTarget->sContent; |
184 | |
|
185 | 0 | if (pTarget->sType == RedactionTargetType::REDACTION_TARGET_PREDEFINED) |
186 | 0 | { |
187 | | //selection_num;selection_name |
188 | 0 | sContent = sContent.getToken(1, ';'); |
189 | 0 | } |
190 | |
|
191 | 0 | m_xControl->set_text(nRowIndex, pTarget->sName, 0); |
192 | 0 | m_xControl->set_text(nRowIndex, getTypeName(pTarget->sType), 1); |
193 | 0 | m_xControl->set_text(nRowIndex, sContent, 2); |
194 | 0 | m_xControl->set_text( |
195 | 0 | nRowIndex, |
196 | 0 | pTarget->bCaseSensitive ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO), 3); |
197 | 0 | m_xControl->set_text( |
198 | 0 | nRowIndex, pTarget->bWholeWords ? SfxResId(STR_REDACTION_YES) : SfxResId(STR_REDACTION_NO), |
199 | 0 | 4); |
200 | 0 | } |
201 | | |
202 | | IMPL_LINK_NOARG(SfxAutoRedactDialog, Load, weld::Button&, void) |
203 | 0 | { |
204 | | //Load a targets list from a previously saved file (a json file?) |
205 | | // ask for filename, where we should load the new config data from |
206 | 0 | StartFileDialog(StartFileDialogType::Open, SfxResId(STR_REDACTION_LOAD_TARGETS)); |
207 | 0 | } |
208 | | |
209 | | IMPL_LINK_NOARG(SfxAutoRedactDialog, Save, weld::Button&, void) |
210 | 0 | { |
211 | | //Allow saving the targets into a file |
212 | 0 | StartFileDialog(StartFileDialogType::SaveAs, SfxResId(STR_REDACTION_SAVE_TARGETS)); |
213 | 0 | } |
214 | | |
215 | | IMPL_LINK_NOARG(SfxAutoRedactDialog, AddHdl, weld::Button&, void) |
216 | 0 | { |
217 | | // Open the Add Target dialog, create a new target and insert into the targets vector and the listbox |
218 | 0 | SfxAddTargetDialog aAddTargetDialog(getDialog(), m_aTargetsBox.GetNameProposal()); |
219 | |
|
220 | 0 | bool bIncomplete; |
221 | 0 | do |
222 | 0 | { |
223 | 0 | bIncomplete = false; |
224 | |
|
225 | 0 | if (aAddTargetDialog.run() != RET_OK) |
226 | 0 | return; |
227 | | |
228 | 0 | if (aAddTargetDialog.getName().isEmpty() |
229 | 0 | || aAddTargetDialog.getType() == RedactionTargetType::REDACTION_TARGET_UNKNOWN |
230 | 0 | || aAddTargetDialog.getContent().isEmpty()) |
231 | 0 | { |
232 | 0 | bIncomplete = true; |
233 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( |
234 | 0 | getDialog(), VclMessageType::Warning, VclButtonsType::Ok, |
235 | 0 | SfxResId(STR_REDACTION_FIELDS_REQUIRED))); |
236 | 0 | xBox->run(); |
237 | 0 | } |
238 | 0 | else if (m_aTargetsBox.GetTargetByName(aAddTargetDialog.getName())) |
239 | 0 | { |
240 | 0 | bIncomplete = true; |
241 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( |
242 | 0 | getDialog(), VclMessageType::Warning, VclButtonsType::Ok, |
243 | 0 | SfxResId(STR_REDACTION_TARGET_NAME_CLASH))); |
244 | 0 | xBox->run(); |
245 | 0 | } |
246 | 0 | else if (aAddTargetDialog.getType() == REDACTION_TARGET_IMAGE |
247 | 0 | && m_aTargetsBox.HasTargetType(REDACTION_TARGET_IMAGE)) |
248 | 0 | { |
249 | 0 | bIncomplete = true; |
250 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( |
251 | 0 | getDialog(), VclMessageType::Warning, VclButtonsType::Ok, |
252 | 0 | SfxResId(STR_REDACTION_MULTI_IMAGE_TARGETS))); |
253 | 0 | xBox->run(); |
254 | 0 | } |
255 | |
|
256 | 0 | } while (bIncomplete); |
257 | | |
258 | | //Alright, we now have everything we need to construct a new target |
259 | 0 | RedactionTarget* redactiontarget = new RedactionTarget( |
260 | 0 | { aAddTargetDialog.getName(), aAddTargetDialog.getType(), aAddTargetDialog.getContent(), |
261 | 0 | aAddTargetDialog.isCaseSensitive(), aAddTargetDialog.isWholeWords(), 0 }); |
262 | | |
263 | | // Only the visual/display part |
264 | 0 | m_aTargetsBox.InsertTarget(redactiontarget); |
265 | | |
266 | | // Actually add to the targets vector |
267 | 0 | if (m_aTargetsBox.GetTargetByName(redactiontarget->sName)) |
268 | 0 | m_aTableTargets.emplace_back(redactiontarget, redactiontarget->sName); |
269 | 0 | else |
270 | 0 | { |
271 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( |
272 | 0 | getDialog(), VclMessageType::Warning, VclButtonsType::Ok, |
273 | 0 | SfxResId(STR_REDACTION_TARGET_ADD_ERROR))); |
274 | 0 | xBox->run(); |
275 | 0 | delete redactiontarget; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | IMPL_LINK_NOARG(SfxAutoRedactDialog, EditHdl, weld::Button&, void) |
280 | 0 | { |
281 | 0 | sal_Int32 nSelectedRow = m_aTargetsBox.get_selected_index(); |
282 | | |
283 | | // No selection, nothing to edit |
284 | 0 | if (nSelectedRow < 0) |
285 | 0 | return; |
286 | | |
287 | | // Only one entry should be selected for editing |
288 | 0 | if (m_aTargetsBox.get_selected_rows().size() > 1) |
289 | 0 | { |
290 | | //Warn the user about multiple selections |
291 | 0 | std::unique_ptr<weld::MessageDialog> xBox( |
292 | 0 | Application::CreateMessageDialog(getDialog(), VclMessageType::Error, VclButtonsType::Ok, |
293 | 0 | SfxResId(STR_REDACTION_MULTI_EDIT))); |
294 | 0 | xBox->run(); |
295 | 0 | return; |
296 | 0 | } |
297 | | |
298 | | // Get the redaction target to be edited |
299 | 0 | RedactionTarget* pTarget = weld::fromId<RedactionTarget*>(m_aTargetsBox.get_id(nSelectedRow)); |
300 | | |
301 | | // Construct and run the edit target dialog |
302 | 0 | SfxAddTargetDialog aEditTargetDialog(getDialog(), pTarget->sName, pTarget->sType, |
303 | 0 | pTarget->sContent, pTarget->bCaseSensitive, |
304 | 0 | pTarget->bWholeWords); |
305 | |
|
306 | 0 | bool bIncomplete; |
307 | 0 | do |
308 | 0 | { |
309 | 0 | bIncomplete = false; |
310 | |
|
311 | 0 | if (aEditTargetDialog.run() != RET_OK) |
312 | 0 | return; |
313 | | |
314 | 0 | if (aEditTargetDialog.getName().isEmpty() |
315 | 0 | || aEditTargetDialog.getType() == RedactionTargetType::REDACTION_TARGET_UNKNOWN |
316 | 0 | || aEditTargetDialog.getContent().isEmpty()) |
317 | 0 | { |
318 | 0 | bIncomplete = true; |
319 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( |
320 | 0 | getDialog(), VclMessageType::Warning, VclButtonsType::Ok, |
321 | 0 | SfxResId(STR_REDACTION_FIELDS_REQUIRED))); |
322 | 0 | xBox->run(); |
323 | 0 | } |
324 | 0 | else if (aEditTargetDialog.getName() != pTarget->sName |
325 | 0 | && m_aTargetsBox.GetTargetByName(aEditTargetDialog.getName())) |
326 | 0 | { |
327 | 0 | bIncomplete = true; |
328 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( |
329 | 0 | getDialog(), VclMessageType::Warning, VclButtonsType::Ok, |
330 | 0 | SfxResId(STR_REDACTION_TARGET_NAME_CLASH))); |
331 | 0 | xBox->run(); |
332 | 0 | } |
333 | 0 | else if (pTarget->sType != REDACTION_TARGET_IMAGE |
334 | 0 | && aEditTargetDialog.getType() == REDACTION_TARGET_IMAGE |
335 | 0 | && m_aTargetsBox.HasTargetType(REDACTION_TARGET_IMAGE)) |
336 | 0 | { |
337 | 0 | bIncomplete = true; |
338 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( |
339 | 0 | getDialog(), VclMessageType::Warning, VclButtonsType::Ok, |
340 | 0 | SfxResId(STR_REDACTION_MULTI_IMAGE_TARGETS))); |
341 | 0 | xBox->run(); |
342 | 0 | } |
343 | |
|
344 | 0 | } while (bIncomplete); |
345 | | |
346 | | // Update the redaction target |
347 | 0 | pTarget->sName = aEditTargetDialog.getName(); |
348 | 0 | pTarget->sType = aEditTargetDialog.getType(); |
349 | 0 | pTarget->sContent = aEditTargetDialog.getContent(); |
350 | 0 | if (pTarget->sType == REDACTION_TARGET_IMAGE) |
351 | 0 | { |
352 | 0 | pTarget->bCaseSensitive = false; |
353 | 0 | pTarget->bWholeWords = false; |
354 | 0 | } |
355 | 0 | else |
356 | 0 | { |
357 | 0 | pTarget->bCaseSensitive = aEditTargetDialog.isCaseSensitive(); |
358 | 0 | pTarget->bWholeWords = aEditTargetDialog.isWholeWords(); |
359 | 0 | } |
360 | | |
361 | | // And sync the targets box row with the actual target data |
362 | 0 | m_aTargetsBox.setRowData(nSelectedRow, pTarget); |
363 | 0 | } |
364 | | IMPL_LINK_NOARG(SfxAutoRedactDialog, DoubleClickEditHdl, weld::TreeView&, bool) |
365 | 0 | { |
366 | 0 | if (m_xEditBtn->get_sensitive()) |
367 | 0 | m_xEditBtn->clicked(); |
368 | 0 | return true; |
369 | 0 | } |
370 | | IMPL_LINK_NOARG(SfxAutoRedactDialog, DeleteHdl, weld::Button&, void) |
371 | 0 | { |
372 | 0 | std::vector<int> aSelectedRows = m_aTargetsBox.get_selected_rows(); |
373 | | |
374 | | //No selection, so nothing to delete |
375 | 0 | if (aSelectedRows.empty()) |
376 | 0 | return; |
377 | | |
378 | 0 | if (aSelectedRows.size() > 1) |
379 | 0 | { |
380 | 0 | OUString sMsg(SfxResId(STR_REDACTION_MULTI_DELETE) |
381 | 0 | .replaceFirst("$(TARGETSCOUNT)", OUString::number(aSelectedRows.size()))); |
382 | | //Warn the user about multiple deletions |
383 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( |
384 | 0 | getDialog(), VclMessageType::Question, VclButtonsType::OkCancel, sMsg)); |
385 | 0 | if (xBox->run() == RET_CANCEL) |
386 | 0 | return; |
387 | 0 | } |
388 | | |
389 | | // After each delete, the indexes of the following items decrease by one. |
390 | 0 | int delta = 0; |
391 | 0 | for (const auto& i : aSelectedRows) |
392 | 0 | { |
393 | 0 | m_aTableTargets.erase(m_aTableTargets.begin() + (i - delta)); |
394 | 0 | m_aTargetsBox.remove(i - delta++); |
395 | 0 | } |
396 | 0 | } |
397 | | |
398 | | namespace |
399 | | { |
400 | | boost::property_tree::ptree redactionTargetToJSON(const RedactionTarget* pTarget) |
401 | 0 | { |
402 | 0 | boost::property_tree::ptree aNode; |
403 | 0 | aNode.put("sName", pTarget->sName.toUtf8().getStr()); |
404 | 0 | aNode.put("eType", pTarget->sType); |
405 | 0 | aNode.put("sContent", pTarget->sContent.toUtf8().getStr()); |
406 | 0 | aNode.put("bWholeWords", pTarget->bWholeWords); |
407 | 0 | aNode.put("bCaseSensitive", pTarget->bCaseSensitive); |
408 | 0 | aNode.put("nID", pTarget->nID); |
409 | |
|
410 | 0 | return aNode; |
411 | 0 | } |
412 | | |
413 | | std::unique_ptr<RedactionTarget> |
414 | | JSONtoRedactionTarget(const boost::property_tree::ptree::value_type& rValue) |
415 | 0 | { |
416 | 0 | OUString sName = OUString::fromUtf8(rValue.second.get<std::string>("sName")); |
417 | 0 | RedactionTargetType eType |
418 | 0 | = static_cast<RedactionTargetType>(atoi(rValue.second.get<std::string>("eType").c_str())); |
419 | 0 | OUString sContent = OUString::fromUtf8(rValue.second.get<std::string>("sContent")); |
420 | 0 | bool bCaseSensitive |
421 | 0 | = OUString::fromUtf8(rValue.second.get<std::string>("bCaseSensitive")).toBoolean(); |
422 | 0 | bool bWholeWords |
423 | 0 | = OUString::fromUtf8(rValue.second.get<std::string>("bWholeWords")).toBoolean(); |
424 | 0 | sal_uInt32 nID = atoi(rValue.second.get<std::string>("nID").c_str()); |
425 | |
|
426 | 0 | return std::unique_ptr<RedactionTarget>( |
427 | 0 | new RedactionTarget{ sName, eType, sContent, bCaseSensitive, bWholeWords, nID }); |
428 | 0 | } |
429 | | } |
430 | | |
431 | | IMPL_LINK_NOARG(SfxAutoRedactDialog, LoadHdl, sfx2::FileDialogHelper*, void) |
432 | 0 | { |
433 | 0 | assert(m_pFileDlg); |
434 | |
|
435 | 0 | OUString sTargetsFile; |
436 | 0 | if (ERRCODE_NONE == m_pFileDlg->GetError()) |
437 | 0 | sTargetsFile = m_pFileDlg->GetPath(); |
438 | |
|
439 | 0 | if (sTargetsFile.isEmpty()) |
440 | 0 | return; |
441 | | |
442 | 0 | OUString sSysPath; |
443 | 0 | osl::File::getSystemPathFromFileURL(sTargetsFile, sSysPath); |
444 | 0 | sTargetsFile = sSysPath; |
445 | |
|
446 | 0 | weld::WaitObject aWaitObject(getDialog()); |
447 | |
|
448 | 0 | try |
449 | 0 | { |
450 | | // Create path string, and read JSON from file |
451 | 0 | std::string sPathStr(OUStringToOString(sTargetsFile, RTL_TEXTENCODING_UTF8)); |
452 | |
|
453 | 0 | boost::property_tree::ptree aTargetsJSON; |
454 | |
|
455 | 0 | boost::property_tree::read_json(sPathStr, aTargetsJSON); |
456 | | |
457 | | // Clear the dialog |
458 | 0 | clearTargets(); |
459 | | |
460 | | // Recreate & add the targets to the dialog |
461 | 0 | for (const boost::property_tree::ptree::value_type& rValue : |
462 | 0 | aTargetsJSON.get_child("RedactionTargets")) |
463 | 0 | { |
464 | 0 | addTarget(JSONtoRedactionTarget(rValue)); |
465 | 0 | } |
466 | 0 | } |
467 | 0 | catch (css::uno::Exception& e) |
468 | 0 | { |
469 | 0 | SAL_WARN("sfx.doc", |
470 | 0 | "Exception caught while trying to load the targets JSON from file: " << e.Message); |
471 | 0 | return; |
472 | | //TODO: Warn the user with a message box |
473 | 0 | } |
474 | 0 | } |
475 | | |
476 | | IMPL_LINK_NOARG(SfxAutoRedactDialog, SaveHdl, sfx2::FileDialogHelper*, void) |
477 | 0 | { |
478 | 0 | assert(m_pFileDlg); |
479 | |
|
480 | 0 | OUString sTargetsFile; |
481 | 0 | if (ERRCODE_NONE == m_pFileDlg->GetError()) |
482 | 0 | sTargetsFile = m_pFileDlg->GetPath(); |
483 | |
|
484 | 0 | if (sTargetsFile.isEmpty()) |
485 | 0 | return; |
486 | | |
487 | 0 | OUString sSysPath; |
488 | 0 | osl::File::getSystemPathFromFileURL(sTargetsFile, sSysPath); |
489 | 0 | sTargetsFile = sSysPath; |
490 | |
|
491 | 0 | weld::WaitObject aWaitObject(getDialog()); |
492 | |
|
493 | 0 | try |
494 | 0 | { |
495 | | // Put the targets into a JSON array |
496 | 0 | boost::property_tree::ptree aTargetsArray; |
497 | 0 | for (const auto& targetPair : m_aTableTargets) |
498 | 0 | { |
499 | 0 | aTargetsArray.push_back( |
500 | 0 | std::make_pair("", redactionTargetToJSON(targetPair.first.get()))); |
501 | 0 | } |
502 | | |
503 | | // Build the JSON tree |
504 | 0 | boost::property_tree::ptree aTargetsTree; |
505 | 0 | aTargetsTree.add_child("RedactionTargets", aTargetsArray); |
506 | | |
507 | | // Create path string, and write JSON to file |
508 | 0 | std::string sPathStr(OUStringToOString(sTargetsFile, RTL_TEXTENCODING_UTF8)); |
509 | |
|
510 | 0 | boost::property_tree::write_json(sPathStr, aTargetsTree); |
511 | 0 | } |
512 | 0 | catch (css::uno::Exception& e) |
513 | 0 | { |
514 | 0 | SAL_WARN("sfx.doc", |
515 | 0 | "Exception caught while trying to save the targets JSON to file: " << e.Message); |
516 | 0 | return; |
517 | | //TODO: Warn the user with a message box |
518 | 0 | } |
519 | 0 | } |
520 | | |
521 | | void SfxAutoRedactDialog::StartFileDialog(StartFileDialogType nType, const OUString& rTitle) |
522 | 0 | { |
523 | 0 | OUString aFilterAllStr(SfxResId(STR_SFX_FILTERNAME_ALL)); |
524 | 0 | OUString aFilterJsonStr(SfxResId(STR_REDACTION_JSON_FILE_FILTER)); |
525 | |
|
526 | 0 | bool bSave = nType == StartFileDialogType::SaveAs; |
527 | 0 | short nDialogType = bSave ? css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION |
528 | 0 | : css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE; |
529 | 0 | m_pFileDlg.reset(new sfx2::FileDialogHelper(nDialogType, FileDialogFlags::NONE, getDialog())); |
530 | |
|
531 | 0 | m_pFileDlg->SetTitle(rTitle); |
532 | 0 | m_pFileDlg->AddFilter(aFilterAllStr, FILEDIALOG_FILTER_ALL); |
533 | 0 | m_pFileDlg->AddFilter(aFilterJsonStr, FILEDIALOG_FILTER_JSON); |
534 | 0 | m_pFileDlg->SetCurrentFilter(aFilterJsonStr); |
535 | |
|
536 | 0 | Link<sfx2::FileDialogHelper*, void> aDlgClosedLink |
537 | 0 | = bSave ? LINK(this, SfxAutoRedactDialog, SaveHdl) |
538 | 0 | : LINK(this, SfxAutoRedactDialog, LoadHdl); |
539 | 0 | m_pFileDlg->SetContext(sfx2::FileDialogHelper::AutoRedact); |
540 | 0 | m_pFileDlg->StartExecuteModal(aDlgClosedLink); |
541 | 0 | } |
542 | | |
543 | | void SfxAutoRedactDialog::addTarget(std::unique_ptr<RedactionTarget> pTarget) |
544 | 0 | { |
545 | | // Only the visual/display part |
546 | 0 | m_aTargetsBox.InsertTarget(pTarget.get()); |
547 | | |
548 | | // Actually add to the targets vector |
549 | 0 | auto name = pTarget->sName; |
550 | 0 | if (m_aTargetsBox.GetTargetByName(name)) |
551 | 0 | m_aTableTargets.emplace_back(std::move(pTarget), name); |
552 | 0 | else |
553 | 0 | { |
554 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog( |
555 | 0 | getDialog(), VclMessageType::Warning, VclButtonsType::Ok, |
556 | 0 | SfxResId(STR_REDACTION_TARGET_ADD_ERROR))); |
557 | 0 | xBox->run(); |
558 | 0 | } |
559 | 0 | } |
560 | | |
561 | | void SfxAutoRedactDialog::clearTargets() |
562 | 0 | { |
563 | | // Clear the targets box |
564 | 0 | m_aTargetsBox.clear(); |
565 | | |
566 | | // Clear the targets vector |
567 | 0 | m_aTableTargets.clear(); |
568 | 0 | } |
569 | | |
570 | | SfxAutoRedactDialog::SfxAutoRedactDialog(weld::Window* pParent) |
571 | 0 | : SfxDialogController(pParent, u"sfx/ui/autoredactdialog.ui"_ustr, u"AutoRedactDialog"_ustr) |
572 | 0 | , m_bIsValidState(true) |
573 | 0 | , m_bTargetsCopied(false) |
574 | 0 | , m_aTargetsBox(m_xBuilder->weld_tree_view(u"targets"_ustr)) |
575 | 0 | , m_xLoadBtn(m_xBuilder->weld_button(u"btnLoadTargets"_ustr)) |
576 | 0 | , m_xSaveBtn(m_xBuilder->weld_button(u"btnSaveTargets"_ustr)) |
577 | 0 | , m_xAddBtn(m_xBuilder->weld_button(u"add"_ustr)) |
578 | 0 | , m_xEditBtn(m_xBuilder->weld_button(u"edit"_ustr)) |
579 | 0 | , m_xDeleteBtn(m_xBuilder->weld_button(u"delete"_ustr)) |
580 | 0 | { |
581 | | // Can be used to remember the last set of redaction targets? |
582 | 0 | OUString sExtraData; |
583 | 0 | SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id()); |
584 | |
|
585 | 0 | if (aDlgOpt.Exists()) |
586 | 0 | { |
587 | 0 | css::uno::Any aUserItem = aDlgOpt.GetUserItem(u"UserItem"_ustr); |
588 | 0 | aUserItem >>= sExtraData; |
589 | 0 | } |
590 | | |
591 | | // update the targets configuration if necessary |
592 | 0 | if (!sExtraData.isEmpty()) |
593 | 0 | { |
594 | 0 | weld::WaitObject aWaitCursor(m_xDialog.get()); |
595 | |
|
596 | 0 | try |
597 | 0 | { |
598 | | // Create path string, and read JSON from file |
599 | 0 | boost::property_tree::ptree aTargetsJSON; |
600 | 0 | std::stringstream aStream(std::string(sExtraData.toUtf8())); |
601 | |
|
602 | 0 | boost::property_tree::read_json(aStream, aTargetsJSON); |
603 | | |
604 | | // Recreate & add the targets to the dialog |
605 | 0 | for (const boost::property_tree::ptree::value_type& rValue : |
606 | 0 | aTargetsJSON.get_child("RedactionTargets")) |
607 | 0 | { |
608 | 0 | addTarget(JSONtoRedactionTarget(rValue)); |
609 | 0 | } |
610 | 0 | } |
611 | 0 | catch (css::uno::Exception& e) |
612 | 0 | { |
613 | 0 | SAL_WARN("sfx.doc", |
614 | 0 | "Exception caught while trying to load the last dialog state: " << e.Message); |
615 | 0 | return; |
616 | | //TODO: Warn the user with a message box |
617 | 0 | } |
618 | 0 | } |
619 | | |
620 | | // Handler connections |
621 | 0 | m_xLoadBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, Load)); |
622 | 0 | m_xSaveBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, Save)); |
623 | 0 | m_xAddBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, AddHdl)); |
624 | 0 | m_xEditBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, EditHdl)); |
625 | 0 | m_xDeleteBtn->connect_clicked(LINK(this, SfxAutoRedactDialog, DeleteHdl)); |
626 | 0 | m_aTargetsBox.connect_row_activated(LINK(this, SfxAutoRedactDialog, DoubleClickEditHdl)); |
627 | 0 | } |
628 | | |
629 | | void SfxAutoRedactDialog::ImplDestroy() |
630 | 0 | { |
631 | 0 | if (m_aTableTargets.empty()) |
632 | 0 | { |
633 | | // Clear the dialog data |
634 | 0 | SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id()); |
635 | 0 | aDlgOpt.Delete(); |
636 | 0 | return; |
637 | 0 | } |
638 | | |
639 | 0 | try |
640 | 0 | { |
641 | | // Put the targets into a JSON array |
642 | 0 | boost::property_tree::ptree aTargetsArray; |
643 | 0 | for (const auto& targetPair : m_aTableTargets) |
644 | 0 | { |
645 | 0 | aTargetsArray.push_back( |
646 | 0 | std::make_pair("", redactionTargetToJSON(targetPair.first.get()))); |
647 | 0 | } |
648 | | |
649 | | // Build the JSON tree |
650 | 0 | boost::property_tree::ptree aTargetsTree; |
651 | 0 | aTargetsTree.add_child("RedactionTargets", aTargetsArray); |
652 | 0 | std::stringstream aStream; |
653 | |
|
654 | 0 | boost::property_tree::write_json(aStream, aTargetsTree, false); |
655 | |
|
656 | 0 | OUString sUserDataStr(OUString::fromUtf8(aStream.str())); |
657 | | |
658 | | // Store the dialog data |
659 | 0 | SvtViewOptions aDlgOpt(EViewType::Dialog, m_xDialog->get_help_id()); |
660 | 0 | aDlgOpt.SetUserItem(u"UserItem"_ustr, css::uno::Any(sUserDataStr)); |
661 | |
|
662 | 0 | if (!m_bTargetsCopied) |
663 | 0 | clearTargets(); |
664 | 0 | } |
665 | 0 | catch (css::uno::Exception& e) |
666 | 0 | { |
667 | 0 | SAL_WARN("sfx.doc", |
668 | 0 | "Exception caught while trying to store the dialog state: " << e.Message); |
669 | 0 | return; |
670 | | //TODO: Warn the user with a message box |
671 | 0 | } |
672 | 0 | } |
673 | | |
674 | 0 | SfxAutoRedactDialog::~SfxAutoRedactDialog() { suppress_fun_call_w_exception(ImplDestroy()); } |
675 | | |
676 | | bool SfxAutoRedactDialog::hasTargets() const |
677 | 0 | { |
678 | | //TODO: Add also some validity checks? |
679 | 0 | if (m_aTableTargets.empty()) |
680 | 0 | return false; |
681 | | |
682 | 0 | return true; |
683 | 0 | } |
684 | | |
685 | | bool SfxAutoRedactDialog::getTargets(std::vector<std::pair<RedactionTarget, OUString>>& r_aTargets) |
686 | 0 | { |
687 | 0 | if (m_aTableTargets.empty()) |
688 | 0 | return true; |
689 | | |
690 | 0 | for (auto const& rPair : m_aTableTargets) |
691 | 0 | r_aTargets.push_back({ *rPair.first, rPair.second }); |
692 | 0 | m_bTargetsCopied = true; |
693 | 0 | return true; |
694 | 0 | } |
695 | | |
696 | | IMPL_LINK_NOARG(SfxAddTargetDialog, SelectTypeHdl, weld::ComboBox&, void) |
697 | 0 | { |
698 | 0 | if (m_xType->get_active_id() == "predefined") |
699 | 0 | { |
700 | | // Hide the usual content widgets |
701 | | // We will just set the id as content |
702 | | // And handle with proper regex in the SfxRedactionHelper |
703 | 0 | m_xLabelContent->set_sensitive(false); |
704 | 0 | m_xLabelContent->set_visible(false); |
705 | 0 | m_xContent->set_sensitive(false); |
706 | 0 | m_xContent->set_visible(false); |
707 | 0 | m_xWholeWords->set_sensitive(false); |
708 | 0 | m_xWholeWords->set_visible(false); |
709 | 0 | m_xCaseSensitive->set_sensitive(false); |
710 | 0 | m_xCaseSensitive->set_visible(false); |
711 | | |
712 | | // And show the predefined targets |
713 | 0 | m_xLabelPredefContent->set_sensitive(true); |
714 | 0 | m_xLabelPredefContent->set_visible(true); |
715 | 0 | m_xPredefContent->set_sensitive(true); |
716 | 0 | m_xPredefContent->set_visible(true); |
717 | 0 | } |
718 | 0 | else if (m_xType->get_active_id() == "image") |
719 | 0 | { |
720 | 0 | m_xLabelContent->set_sensitive(false); |
721 | 0 | m_xLabelContent->set_visible(false); |
722 | 0 | m_xContent->set_sensitive(false); |
723 | 0 | m_xContent->set_visible(false); |
724 | 0 | m_xWholeWords->set_sensitive(false); |
725 | 0 | m_xWholeWords->set_visible(false); |
726 | 0 | m_xCaseSensitive->set_sensitive(false); |
727 | 0 | m_xCaseSensitive->set_visible(false); |
728 | |
|
729 | 0 | m_xLabelPredefContent->set_sensitive(false); |
730 | 0 | m_xLabelPredefContent->set_visible(false); |
731 | 0 | m_xPredefContent->set_sensitive(false); |
732 | 0 | m_xPredefContent->set_visible(false); |
733 | 0 | } |
734 | 0 | else |
735 | 0 | { |
736 | 0 | m_xLabelPredefContent->set_sensitive(false); |
737 | 0 | m_xLabelPredefContent->set_visible(false); |
738 | 0 | m_xPredefContent->set_sensitive(false); |
739 | 0 | m_xPredefContent->set_visible(false); |
740 | |
|
741 | 0 | m_xLabelContent->set_sensitive(true); |
742 | 0 | m_xLabelContent->set_visible(true); |
743 | 0 | m_xContent->set_sensitive(true); |
744 | 0 | m_xContent->set_visible(true); |
745 | 0 | m_xWholeWords->set_sensitive(true); |
746 | 0 | m_xWholeWords->set_visible(true); |
747 | 0 | m_xCaseSensitive->set_sensitive(true); |
748 | 0 | m_xCaseSensitive->set_visible(true); |
749 | 0 | } |
750 | 0 | } |
751 | | |
752 | | SfxAddTargetDialog::SfxAddTargetDialog(weld::Window* pParent, const OUString& rName) |
753 | 0 | : GenericDialogController(pParent, u"sfx/ui/addtargetdialog.ui"_ustr, u"AddTargetDialog"_ustr) |
754 | 0 | , m_xName(m_xBuilder->weld_entry(u"name"_ustr)) |
755 | 0 | , m_xType(m_xBuilder->weld_combo_box(u"type"_ustr)) |
756 | 0 | , m_xLabelContent(m_xBuilder->weld_label(u"label_content"_ustr)) |
757 | 0 | , m_xContent(m_xBuilder->weld_entry(u"content"_ustr)) |
758 | 0 | , m_xLabelPredefContent(m_xBuilder->weld_label(u"label_content_predef"_ustr)) |
759 | 0 | , m_xPredefContent(m_xBuilder->weld_combo_box(u"content_predef"_ustr)) |
760 | 0 | , m_xCaseSensitive(m_xBuilder->weld_check_button(u"checkboxCaseSensitive"_ustr)) |
761 | 0 | , m_xWholeWords(m_xBuilder->weld_check_button(u"checkboxWholeWords"_ustr)) |
762 | 0 | { |
763 | 0 | m_xName->set_text(rName); |
764 | 0 | m_xName->select_region(0, rName.getLength()); |
765 | |
|
766 | 0 | m_xType->connect_changed(LINK(this, SfxAddTargetDialog, SelectTypeHdl)); |
767 | 0 | } |
768 | | |
769 | | SfxAddTargetDialog::SfxAddTargetDialog(weld::Window* pParent, const OUString& sName, |
770 | | const RedactionTargetType& eTargetType, |
771 | | const OUString& sContent, bool bCaseSensitive, |
772 | | bool bWholeWords) |
773 | 0 | : GenericDialogController(pParent, u"sfx/ui/addtargetdialog.ui"_ustr, u"AddTargetDialog"_ustr) |
774 | 0 | , m_xName(m_xBuilder->weld_entry(u"name"_ustr)) |
775 | 0 | , m_xType(m_xBuilder->weld_combo_box(u"type"_ustr)) |
776 | 0 | , m_xLabelContent(m_xBuilder->weld_label(u"label_content"_ustr)) |
777 | 0 | , m_xContent(m_xBuilder->weld_entry(u"content"_ustr)) |
778 | 0 | , m_xLabelPredefContent(m_xBuilder->weld_label(u"label_content_predef"_ustr)) |
779 | 0 | , m_xPredefContent(m_xBuilder->weld_combo_box(u"content_predef"_ustr)) |
780 | 0 | , m_xCaseSensitive(m_xBuilder->weld_check_button(u"checkboxCaseSensitive"_ustr)) |
781 | 0 | , m_xWholeWords(m_xBuilder->weld_check_button(u"checkboxWholeWords"_ustr)) |
782 | 0 | { |
783 | 0 | m_xName->set_text(sName); |
784 | 0 | m_xName->select_region(0, sName.getLength()); |
785 | |
|
786 | 0 | m_xType->set_active_id(getTypeID(eTargetType)); |
787 | 0 | m_xType->connect_changed(LINK(this, SfxAddTargetDialog, SelectTypeHdl)); |
788 | |
|
789 | 0 | if (eTargetType == RedactionTargetType::REDACTION_TARGET_PREDEFINED) |
790 | 0 | { |
791 | 0 | SelectTypeHdl(*m_xPredefContent); |
792 | 0 | m_xPredefContent->set_active(o3tl::toInt32(o3tl::getToken(sContent, 0, ';'))); |
793 | 0 | } |
794 | 0 | else if (eTargetType == RedactionTargetType::REDACTION_TARGET_IMAGE) |
795 | 0 | { |
796 | 0 | m_xContent->set_visible(false); |
797 | 0 | m_xLabelContent->set_visible(false); |
798 | 0 | m_xCaseSensitive->set_visible(false); |
799 | 0 | m_xWholeWords->set_visible(false); |
800 | 0 | } |
801 | 0 | else |
802 | 0 | { |
803 | 0 | m_xContent->set_text(sContent); |
804 | 0 | } |
805 | |
|
806 | 0 | m_xCaseSensitive->set_active(bCaseSensitive); |
807 | 0 | m_xWholeWords->set_active(bWholeWords); |
808 | |
|
809 | 0 | set_title(SfxResId(STR_REDACTION_EDIT_TARGET)); |
810 | 0 | } |
811 | | |
812 | | RedactionTargetType SfxAddTargetDialog::getType() const |
813 | 0 | { |
814 | 0 | OUString sTypeID = m_xType->get_active_id(); |
815 | |
|
816 | 0 | if (sTypeID == "text") |
817 | 0 | return RedactionTargetType::REDACTION_TARGET_TEXT; |
818 | 0 | else if (sTypeID == "regex") |
819 | 0 | return RedactionTargetType::REDACTION_TARGET_REGEX; |
820 | 0 | else if (sTypeID == "predefined") |
821 | 0 | return RedactionTargetType::REDACTION_TARGET_PREDEFINED; |
822 | 0 | else if (sTypeID == "image") |
823 | 0 | return RedactionTargetType::REDACTION_TARGET_IMAGE; |
824 | 0 | else |
825 | 0 | return RedactionTargetType::REDACTION_TARGET_UNKNOWN; |
826 | 0 | } |
827 | | |
828 | | OUString SfxAddTargetDialog::getContent() const |
829 | 0 | { |
830 | 0 | if (m_xType->get_active_id() == "predefined") |
831 | 0 | { |
832 | 0 | return OUString(OUString::number(m_xPredefContent->get_active()) + ";" |
833 | 0 | + m_xPredefContent->get_active_text()); |
834 | 0 | } |
835 | 0 | else if (m_xType->get_active_id() == "image") |
836 | 0 | return "All Images"; |
837 | | |
838 | 0 | return m_xContent->get_text(); |
839 | 0 | } |
840 | | |
841 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |