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