Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */