1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 
11 #include <svx/ClassificationDialog.hxx>
12 #include <svx/ClassificationCommon.hxx>
13 
14 #include <editeng/flditem.hxx>
15 #include <editeng/eeitem.hxx>
16 #include <editeng/section.hxx>
17 #include <editeng/editobj.hxx>
18 #include <editeng/wghtitem.hxx>
19 #include <svl/itemset.hxx>
20 #include <osl/file.hxx>
21 #include <rtl/bootstrap.hxx>
22 #include <config_folders.h>
23 #include <tools/stream.hxx>
24 #include <tools/XmlWriter.hxx>
25 #include <tools/XmlWalker.hxx>
26 #include <vcl/customweld.hxx>
27 #include <vcl/event.hxx>
28 #include <vcl/svapp.hxx>
29 #include <sfx2/objsh.hxx>
30 
31 #include <officecfg/Office/Common.hxx>
32 
33 #include "ClassificationEditView.hxx"
34 
35 namespace svx {
36 
IMPL_STATIC_LINK(ClassificationDialog,KeyInput,const KeyEvent &,rKeyEvent,bool)37 IMPL_STATIC_LINK(ClassificationDialog, KeyInput, const KeyEvent&, rKeyEvent, bool)
38 {
39     bool bTextIsFreeForm = officecfg::Office::Common::Classification::IntellectualPropertyTextInputIsFreeForm::get();
40 
41     if (!bTextIsFreeForm)
42     {
43         // Ignore key combination with modifier keys
44         if (rKeyEvent.GetKeyCode().IsMod3()
45          || rKeyEvent.GetKeyCode().IsMod2()
46          || rKeyEvent.GetKeyCode().IsMod1())
47         {
48             return true;
49         }
50 
51         switch (rKeyEvent.GetKeyCode().GetCode())
52         {
53             // Allowed characters
54             case KEY_BACKSPACE:
55             case KEY_DELETE:
56             case KEY_DIVIDE:
57             case KEY_SEMICOLON:
58             case KEY_SPACE:
59                 return false;
60             // Anything else is ignored
61             default:
62                 return true;
63         }
64     }
65 
66     return false;
67 }
68 
69 namespace {
70 
71 constexpr size_t RECENTLY_USED_LIMIT = 5;
72 
73 constexpr OUStringLiteral constRecentlyUsedFileName(u"recentlyUsed.xml");
74 
lcl_getClassificationUserPath()75 OUString lcl_getClassificationUserPath()
76 {
77     OUString sPath("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/user/classification/");
78     rtl::Bootstrap::expandMacros(sPath);
79     return sPath;
80 }
81 
findField(editeng::Section const & rSection)82 const SvxFieldItem* findField(editeng::Section const & rSection)
83 {
84     for (SfxPoolItem const * pPool : rSection.maAttributes)
85     {
86         if (pPool->Which() == EE_FEATURE_FIELD)
87             return static_cast<const SvxFieldItem*>(pPool);
88     }
89     return nullptr;
90 }
91 
fileExists(OUString const & sFilename)92 bool fileExists(OUString const & sFilename)
93 {
94     osl::File aFile(sFilename);
95     osl::FileBase::RC eRC = aFile.open(osl_File_OpenFlag_Read);
96     return osl::FileBase::E_None == eRC;
97 }
98 
stringToClassificationType(std::string_view rsType,svx::ClassificationType & reType)99 bool stringToClassificationType(std::string_view rsType, svx::ClassificationType & reType)
100 {
101     if (rsType == "CATEGORY")
102         reType = svx::ClassificationType::CATEGORY;
103     else if (rsType == "INTELLECTUAL_PROPERTY_PART")
104         reType = svx::ClassificationType::INTELLECTUAL_PROPERTY_PART;
105     else if (rsType == "MARKING")
106         reType = svx::ClassificationType::MARKING;
107     else if (rsType == "PARAGRAPH")
108         reType = svx::ClassificationType::PARAGRAPH;
109     else if (rsType == "TEXT")
110         reType = svx::ClassificationType::TEXT;
111     else
112         return false;
113     return true;
114 }
115 
classificationTypeToString(svx::ClassificationType const & reType)116 OUString classificationTypeToString(svx::ClassificationType const & reType)
117 {
118     switch(reType)
119     {
120         case svx::ClassificationType::CATEGORY:
121             return "CATEGORY"; break;
122         case svx::ClassificationType::MARKING:
123             return "MARKING"; break;
124         case svx::ClassificationType::TEXT:
125             return "TEXT"; break;
126         case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
127             return "INTELLECTUAL_PROPERTY_PART"; break;
128         case svx::ClassificationType::PARAGRAPH:
129             return "PARAGRAPH"; break;
130     }
131     return OUString();
132 }
133 
writeResultToXml(tools::XmlWriter & rXmlWriter,std::vector<ClassificationResult> const & rResultCollection)134 void writeResultToXml(tools::XmlWriter & rXmlWriter,
135                       std::vector<ClassificationResult> const & rResultCollection)
136 {
137     for (ClassificationResult const & rResult : rResultCollection)
138     {
139         rXmlWriter.startElement("element");
140         OUString sType = classificationTypeToString(rResult.meType);
141         rXmlWriter.attribute("type", sType);
142         rXmlWriter.startElement("string");
143         rXmlWriter.content(rResult.msName);
144         rXmlWriter.endElement();
145         rXmlWriter.startElement("abbreviatedString");
146         rXmlWriter.content(rResult.msAbbreviatedName);
147         rXmlWriter.endElement();
148         rXmlWriter.startElement("identifier");
149         rXmlWriter.content(rResult.msIdentifier);
150         rXmlWriter.endElement();
151         rXmlWriter.endElement();
152     }
153 }
154 
155 } // end anonymous namespace
156 
ClassificationDialog(weld::Window * pParent,const bool bPerParagraph,const std::function<void ()> & rParagraphSignHandler)157 ClassificationDialog::ClassificationDialog(weld::Window* pParent, const bool bPerParagraph, const std::function<void()>& rParagraphSignHandler)
158     : GenericDialogController(pParent, "svx/ui/classificationdialog.ui", "AdvancedDocumentClassificationDialog")
159     , maHelper(SfxObjectShell::Current()->getDocProperties())
160     , maInternationalHelper(SfxObjectShell::Current()->getDocProperties(), /*bUseLocalizedPolicy*/ false)
161     , m_bPerParagraph(bPerParagraph)
162     , m_aParagraphSignHandler(rParagraphSignHandler)
163     , m_nCurrentSelectedCategory(-1)
164     , m_xOkButton(m_xBuilder->weld_button("ok"))
165     , m_xSignButton(m_xBuilder->weld_button("signButton"))
166     , m_xToolBox(m_xBuilder->weld_toggle_button("toolbox"))
167     , m_xRecentlyUsedListBox(m_xBuilder->weld_combo_box("recentlyUsedCB"))
168     , m_xClassificationListBox(m_xBuilder->weld_combo_box("classificationCB"))
169     , m_xInternationalClassificationListBox(m_xBuilder->weld_combo_box("internationalClassificationCB"))
170     , m_xMarkingLabel(m_xBuilder->weld_label("markingLabel"))
171     , m_xMarkingListBox(m_xBuilder->weld_tree_view("markingLB"))
172     , m_xIntellectualPropertyPartListBox(m_xBuilder->weld_tree_view("intellectualPropertyPartLB"))
173     , m_xIntellectualPropertyPartNumberListBox(m_xBuilder->weld_tree_view("intellectualPropertyPartNumberLB"))
174     , m_xIntellectualPropertyPartAddButton(m_xBuilder->weld_button("intellectualPropertyPartAddButton"))
175     , m_xIntellectualPropertyPartEdit(m_xBuilder->weld_entry("intellectualPropertyPartEntry"))
176     , m_xIntellectualPropertyExpander(m_xBuilder->weld_expander("intellectualPropertyExpander"))
177     , m_xEditWindow(new ClassificationEditView)
178     , m_xEditWindowWeld(new weld::CustomWeld(*m_xBuilder, "classificationEditWindow", *m_xEditWindow))
179 {
180     m_xOkButton->connect_clicked(LINK(this, ClassificationDialog, OkHdl));
181     m_xSignButton->connect_clicked(LINK(this, ClassificationDialog, ButtonClicked));
182     m_xSignButton->set_visible(m_bPerParagraph);
183 
184     m_xIntellectualPropertyPartEdit->connect_key_press(LINK(this, ClassificationDialog, KeyInput));
185 
186     // no need for BOLD if we do paragraph classification
187     if (m_bPerParagraph)
188     {
189         m_xToolBox->hide();
190     }
191     else
192     {
193         m_xToolBox->connect_toggled(LINK(this, ClassificationDialog, SelectToolboxHdl));
194     }
195 
196     m_xIntellectualPropertyPartAddButton->connect_clicked(LINK(this, ClassificationDialog, ButtonClicked));
197 
198     m_xClassificationListBox->set_size_request(m_xClassificationListBox->get_approximate_digit_width() * 20, -1);
199     m_xClassificationListBox->connect_changed(LINK(this, ClassificationDialog, SelectClassificationHdl));
200     for (const OUString& rName : maHelper.GetBACNames())
201         m_xClassificationListBox->append_text(rName);
202 
203     m_xInternationalClassificationListBox->set_size_request(m_xInternationalClassificationListBox->get_approximate_digit_width() * 20, -1);
204     m_xInternationalClassificationListBox->connect_changed(LINK(this, ClassificationDialog, SelectClassificationHdl));
205     for (const OUString& rName : maInternationalHelper.GetBACNames())
206         m_xInternationalClassificationListBox->append_text(rName);
207 
208     if (!maHelper.GetMarkings().empty())
209     {
210         m_xMarkingListBox->set_size_request(m_xMarkingListBox->get_approximate_digit_width() * 10,
211                                             m_xMarkingListBox->get_height_rows(4));
212         m_xMarkingListBox->connect_row_activated(LINK(this, ClassificationDialog, SelectMarkingHdl));
213 
214         for (const OUString& rName : maHelper.GetMarkings())
215             m_xMarkingListBox->append_text(rName);
216     }
217     else
218     {
219         m_xMarkingListBox->hide();
220         m_xMarkingLabel->hide();
221     }
222 
223     m_xIntellectualPropertyPartNumberListBox->set_size_request(m_xIntellectualPropertyPartNumberListBox->get_approximate_digit_width() * 10,
224                                                                m_xIntellectualPropertyPartNumberListBox->get_height_rows(5));
225     m_xIntellectualPropertyPartNumberListBox->connect_row_activated(LINK(this, ClassificationDialog, SelectIPPartNumbersHdl));
226     for (const OUString& rName : maHelper.GetIntellectualPropertyPartNumbers())
227         m_xIntellectualPropertyPartNumberListBox->append_text(rName);
228 
229     m_xIntellectualPropertyPartNumberListBox->set_size_request(m_xIntellectualPropertyPartNumberListBox->get_approximate_digit_width() * 20,
230                                                                m_xIntellectualPropertyPartListBox->get_height_rows(5));
231     m_xIntellectualPropertyPartListBox->connect_row_activated(LINK(this, ClassificationDialog, SelectIPPartHdl));
232     for (const OUString& rName : maHelper.GetIntellectualPropertyParts())
233         m_xIntellectualPropertyPartListBox->append_text(rName);
234 
235     m_xRecentlyUsedListBox->set_size_request(m_xRecentlyUsedListBox->get_approximate_digit_width() * 5, -1);
236     m_xRecentlyUsedListBox->connect_changed(LINK(this, ClassificationDialog, SelectRecentlyUsedHdl));
237 
238     m_xIntellectualPropertyExpander->connect_expanded(LINK(this, ClassificationDialog, ExpandedHdl));
239     if (officecfg::Office::Common::Classification::IntellectualPropertySectionExpanded::get())
240         m_nAsyncExpandEvent = Application::PostUserEvent(LINK(this, ClassificationDialog, OnAsyncExpandHdl));
241     else
242         m_nAsyncExpandEvent = nullptr;
243 
244     m_xEditWindow->SetModifyHdl(LINK(this, ClassificationDialog, EditWindowModifiedHdl));
245 
246     readRecentlyUsed();
247     toggleWidgetsDependingOnCategory();
248 
249     int nNumber = 1;
250     if (m_aRecentlyUsedValuesCollection.empty())
251     {
252         m_xRecentlyUsedListBox->set_sensitive(false);
253     }
254     else
255     {
256         for (std::vector<ClassificationResult> const & rResults : m_aRecentlyUsedValuesCollection)
257         {
258             OUString rContentRepresentation = svx::classification::convertClassificationResultToString(rResults);
259             OUString rDescription = OUString::number(nNumber) + ": " + rContentRepresentation;
260             nNumber++;
261 
262             m_xRecentlyUsedListBox->append_text(rDescription);
263         }
264     }
265 }
266 
267 //do it async so gtk has a chance to shrink it to best size, otherwise its larger than min
IMPL_LINK_NOARG(ClassificationDialog,OnAsyncExpandHdl,void *,void)268 IMPL_LINK_NOARG(ClassificationDialog, OnAsyncExpandHdl, void*, void)
269 {
270     m_nAsyncExpandEvent = nullptr;
271     m_xIntellectualPropertyExpander->set_expanded(true);
272 }
273 
~ClassificationDialog()274 ClassificationDialog::~ClassificationDialog()
275 {
276     if (m_nAsyncExpandEvent)
277         Application::RemoveUserEvent(m_nAsyncExpandEvent);
278 }
279 
insertCategoryField(sal_Int32 nID)280 void ClassificationDialog::insertCategoryField(sal_Int32 nID)
281 {
282     const OUString aFullString = maHelper.GetBACNames()[nID];
283     const OUString aAbbreviatedString = maHelper.GetAbbreviatedBACNames()[nID];
284     const OUString aIdentifierString = maHelper.GetBACIdentifiers()[nID];
285     insertField(ClassificationType::CATEGORY, aAbbreviatedString, aFullString, aIdentifierString);
286 }
287 
insertField(ClassificationType eType,OUString const & rString,OUString const & rFullString,OUString const & rIdentifier)288 void ClassificationDialog::insertField(ClassificationType eType, OUString const & rString, OUString const & rFullString, OUString const & rIdentifier)
289 {
290     ClassificationField aField(eType, rString, rFullString, rIdentifier);
291     m_xEditWindow->InsertField(SvxFieldItem(aField, EE_FEATURE_FIELD));
292 }
293 
setupValues(std::vector<ClassificationResult> const & rInput)294 void ClassificationDialog::setupValues(std::vector<ClassificationResult> const & rInput)
295 {
296     m_aInitialValues = rInput;
297     readIn(m_aInitialValues);
298 }
299 
readRecentlyUsed()300 void ClassificationDialog::readRecentlyUsed()
301 {
302     OUString sPath = lcl_getClassificationUserPath();
303     OUString sFilePath(sPath + constRecentlyUsedFileName);
304 
305     if (!fileExists(sFilePath))
306         return;
307 
308     SvFileStream aFileStream(sFilePath, StreamMode::READ);
309     tools::XmlWalker aWalker;
310     if (!aWalker.open(&aFileStream))
311         return;
312 
313     if (aWalker.name() != "recentlyUsedClassifications")
314         return;
315 
316     aWalker.children();
317     while (aWalker.isValid())
318     {
319         if (aWalker.name() == "elementGroup")
320         {
321             std::vector<ClassificationResult> aResults;
322 
323             aWalker.children();
324 
325             while (aWalker.isValid())
326             {
327                 if (aWalker.name() == "element")
328                 {
329                     svx::ClassificationType eType = svx::ClassificationType::TEXT;
330                     OUString sString;
331                     OUString sAbbreviatedString;
332                     OUString sIdentifier;
333 
334                     // Convert string to classification type, but continue only if
335                     // conversion was successful.
336                     if (stringToClassificationType(aWalker.attribute("type"), eType))
337                     {
338                         aWalker.children();
339 
340                         while (aWalker.isValid())
341                         {
342                             if (aWalker.name() == "string")
343                             {
344                                 sString = OStringToOUString(aWalker.content(), RTL_TEXTENCODING_UTF8);
345                             }
346                             else if (aWalker.name() == "abbreviatedString")
347                             {
348                                 sAbbreviatedString = OStringToOUString(aWalker.content(), RTL_TEXTENCODING_UTF8);
349                             }
350                             else if (aWalker.name() == "identifier")
351                             {
352                                 sIdentifier = OStringToOUString(aWalker.content(), RTL_TEXTENCODING_UTF8);
353                             }
354                             aWalker.next();
355                         }
356                         aWalker.parent();
357 
358                         aResults.push_back({ eType, sString, sAbbreviatedString, sIdentifier });
359                     }
360                 }
361                 aWalker.next();
362             }
363             aWalker.parent();
364             m_aRecentlyUsedValuesCollection.push_back(aResults);
365         }
366         aWalker.next();
367     }
368     aWalker.parent();
369 }
370 
writeRecentlyUsed()371 void ClassificationDialog::writeRecentlyUsed()
372 {
373     OUString sPath = lcl_getClassificationUserPath();
374     osl::Directory::createPath(sPath);
375     OUString sFilePath(sPath + constRecentlyUsedFileName);
376 
377     std::unique_ptr<SvStream> pStream;
378     pStream.reset(new SvFileStream(sFilePath, StreamMode::STD_READWRITE | StreamMode::TRUNC));
379 
380     tools::XmlWriter aXmlWriter(pStream.get());
381 
382     if (!aXmlWriter.startDocument())
383         return;
384 
385     aXmlWriter.startElement("recentlyUsedClassifications");
386 
387     aXmlWriter.startElement("elementGroup");
388 
389     writeResultToXml(aXmlWriter, getResult());
390 
391     aXmlWriter.endElement();
392 
393     if (m_aRecentlyUsedValuesCollection.size() >= RECENTLY_USED_LIMIT)
394         m_aRecentlyUsedValuesCollection.pop_back();
395 
396     for (std::vector<ClassificationResult> const & rResultCollection : m_aRecentlyUsedValuesCollection)
397     {
398         aXmlWriter.startElement("elementGroup");
399 
400         writeResultToXml(aXmlWriter, rResultCollection);
401 
402         aXmlWriter.endElement();
403     }
404 
405     aXmlWriter.endElement();
406 
407     aXmlWriter.endDocument();
408 }
409 
readIn(std::vector<ClassificationResult> const & rInput)410 void ClassificationDialog::readIn(std::vector<ClassificationResult> const & rInput)
411 {
412     sal_Int32 nParagraph = -1;
413 
414     for (ClassificationResult const & rClassificationResult : rInput)
415     {
416 
417         switch (rClassificationResult.meType)
418         {
419             case svx::ClassificationType::TEXT:
420             {
421                 m_xEditWindow->getEditView().InsertText(rClassificationResult.msName);
422             }
423             break;
424 
425             case svx::ClassificationType::CATEGORY:
426             {
427                 OUString sName;
428                 if (rClassificationResult.msName.isEmpty())
429                     sName = maHelper.GetBACNameForIdentifier(rClassificationResult.msIdentifier);
430                 else
431                     sName = rClassificationResult.msName;
432 
433                 OUString sAbbreviatedName = rClassificationResult.msAbbreviatedName;
434                 if (sAbbreviatedName.isEmpty())
435                     sAbbreviatedName = maHelper.GetAbbreviatedBACName(sName);
436 
437                 m_xClassificationListBox->set_active_text(sName);
438                 m_nCurrentSelectedCategory = m_xClassificationListBox->get_active();
439                 m_xInternationalClassificationListBox->set_active(m_xClassificationListBox->get_active());
440 
441                 insertField(rClassificationResult.meType, sAbbreviatedName, sName, rClassificationResult.msIdentifier);
442             }
443             break;
444 
445             case svx::ClassificationType::MARKING:
446             {
447                 m_xMarkingListBox->select_text(rClassificationResult.msName);
448                 insertField(rClassificationResult.meType, rClassificationResult.msName, rClassificationResult.msName, rClassificationResult.msIdentifier);
449             }
450             break;
451 
452             case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
453             {
454                 insertField(rClassificationResult.meType, rClassificationResult.msName, rClassificationResult.msName, rClassificationResult.msIdentifier);
455             }
456             break;
457 
458             case svx::ClassificationType::PARAGRAPH:
459             {
460                 nParagraph++;
461 
462                 if (nParagraph != 0)
463                     m_xEditWindow->getEditView().InsertParaBreak();
464 
465                 // Set paragraph font weight
466                 FontWeight eWeight = (rClassificationResult.msName == "BOLD") ? WEIGHT_BOLD : WEIGHT_NORMAL;
467 
468                 ClassificationEditEngine& rEdEngine = m_xEditWindow->getEditEngine();
469                 SfxItemSet aSet(rEdEngine.GetParaAttribs(nParagraph));
470                 aSet.Put(SvxWeightItem(eWeight, EE_CHAR_WEIGHT));
471                 rEdEngine.SetParaAttribs(nParagraph, aSet);
472             }
473             break;
474 
475             default:
476             break;
477         }
478     }
479     toggleWidgetsDependingOnCategory();
480 }
481 
toggleWidgetsDependingOnCategory()482 void ClassificationDialog::toggleWidgetsDependingOnCategory()
483 {
484     const EditEngine& rEditEngine = m_xEditWindow->getEditEngine();
485 
486     for (sal_Int32 nParagraph = 0; nParagraph < rEditEngine.GetParagraphCount(); ++nParagraph)
487     {
488         sal_uInt16 nFieldCount = rEditEngine.GetFieldCount(nParagraph);
489         for (sal_uInt16 nField = 0; nField < nFieldCount; ++nField)
490         {
491             EFieldInfo aFieldInfo = rEditEngine.GetFieldInfo(nParagraph, nField);
492             if (aFieldInfo.pFieldItem)
493             {
494                 const ClassificationField* pClassificationField = dynamic_cast<const ClassificationField*>(aFieldInfo.pFieldItem->GetField());
495                 if (pClassificationField && pClassificationField->meType == ClassificationType::CATEGORY)
496                 {
497                     m_xOkButton->set_sensitive(true);
498                     return;
499                 }
500             }
501         }
502     }
503 
504     // Category field in the text edit has been deleted, so reset the list boxes
505     m_xOkButton->set_sensitive(false);
506     m_xClassificationListBox->set_active(-1);
507     m_xInternationalClassificationListBox->set_active(-1);
508 }
509 
getResult()510 std::vector<ClassificationResult> ClassificationDialog::getResult()
511 {
512     std::vector<ClassificationResult> aClassificationResults;
513 
514     ClassificationEditEngine& rEdEngine = m_xEditWindow->getEditEngine();
515     std::unique_ptr<EditTextObject> pEditText(rEdEngine.CreateTextObject());
516 
517     sal_Int32 nCurrentParagraph = -1;
518 
519     std::vector<editeng::Section> aSections;
520     pEditText->GetAllSections(aSections);
521     for (editeng::Section const & rSection : aSections)
522     {
523         while (nCurrentParagraph < rSection.mnParagraph)
524         {
525             nCurrentParagraph++;
526 
527             // Get Weight of current paragraph
528             FontWeight eFontWeight = WEIGHT_NORMAL;
529             SfxItemSet aItemSet(rEdEngine.GetParaAttribs(nCurrentParagraph));
530             if (const SfxPoolItem* pItem = aItemSet.GetItem(EE_CHAR_WEIGHT, false))
531             {
532                 const SvxWeightItem* pWeightItem = dynamic_cast<const SvxWeightItem*>(pItem);
533                 if (pWeightItem && pWeightItem->GetWeight() == WEIGHT_BOLD)
534                     eFontWeight = WEIGHT_BOLD;
535             }
536             // Font weight to string
537             OUString sWeightProperty = "NORMAL";
538             if (eFontWeight == WEIGHT_BOLD)
539                 sWeightProperty = "BOLD";
540             // Insert into collection
541             OUString sBlank;
542             aClassificationResults.push_back({ ClassificationType::PARAGRAPH, sWeightProperty, sBlank, sBlank });
543         }
544 
545         const SvxFieldItem* pFieldItem = findField(rSection);
546 
547         ESelection aSelection(rSection.mnParagraph, rSection.mnStart, rSection.mnParagraph, rSection.mnEnd);
548         const OUString sDisplayString = rEdEngine.GetText(aSelection);
549         if (!sDisplayString.isEmpty())
550         {
551             const ClassificationField* pClassificationField = pFieldItem ? dynamic_cast<const ClassificationField*>(pFieldItem->GetField()) : nullptr;
552 
553             if (pClassificationField)
554             {
555                 aClassificationResults.push_back({ pClassificationField->meType, pClassificationField->msFullClassName,
556                                                    pClassificationField->msDescription, pClassificationField->msIdentifier });
557             }
558             else
559             {
560                 aClassificationResults.push_back({ ClassificationType::TEXT, sDisplayString, sDisplayString, OUString() });
561             }
562         }
563     }
564 
565     return aClassificationResults;
566 }
567 
IMPL_LINK(ClassificationDialog,SelectClassificationHdl,weld::ComboBox &,rBox,void)568 IMPL_LINK(ClassificationDialog, SelectClassificationHdl, weld::ComboBox&, rBox, void)
569 {
570     const sal_Int32 nSelected = rBox.get_active();
571     if (nSelected < 0 || m_nCurrentSelectedCategory == nSelected)
572         return;
573 
574     std::unique_ptr<EditTextObject> pEditText(m_xEditWindow->getEditEngine().CreateTextObject());
575     std::vector<editeng::Section> aSections;
576     pEditText->GetAllSections(aSections);
577 
578     // if we are replacing an existing field
579     bool bReplaceExisting = false;
580     // selection of the existing field, which will be replaced
581     ESelection aExistingFieldSelection;
582 
583     for (editeng::Section const & rSection : aSections)
584     {
585         const SvxFieldItem* pFieldItem = findField(rSection);
586         if (pFieldItem)
587         {
588             const ClassificationField* pClassificationField = dynamic_cast<const ClassificationField*>(pFieldItem->GetField());
589             if (pClassificationField && pClassificationField->meType == ClassificationType::CATEGORY)
590             {
591                 aExistingFieldSelection = ESelection(rSection.mnParagraph, rSection.mnStart,
592                                                      rSection.mnParagraph, rSection.mnEnd);
593                 bReplaceExisting = true;
594             }
595         }
596     }
597 
598     if (bReplaceExisting)
599         m_xEditWindow->getEditView().SetSelection(aExistingFieldSelection);
600 
601     insertCategoryField(nSelected);
602 
603     // Change category to the new selection
604     m_xInternationalClassificationListBox->set_active(nSelected);
605     m_xClassificationListBox->set_active(nSelected);
606     m_nCurrentSelectedCategory = nSelected;
607 }
608 
IMPL_LINK(ClassificationDialog,SelectMarkingHdl,weld::TreeView &,rBox,bool)609 IMPL_LINK(ClassificationDialog, SelectMarkingHdl, weld::TreeView&, rBox, bool)
610 {
611     sal_Int32 nSelected = rBox.get_selected_index();
612     if (nSelected >= 0)
613     {
614         const OUString aString = maHelper.GetMarkings()[nSelected];
615         insertField(ClassificationType::MARKING, aString, aString);
616     }
617     return true;
618 }
619 
IMPL_LINK(ClassificationDialog,SelectIPPartNumbersHdl,weld::TreeView &,rBox,bool)620 IMPL_LINK(ClassificationDialog, SelectIPPartNumbersHdl, weld::TreeView&, rBox, bool)
621 {
622     sal_Int32 nSelected = rBox.get_selected_index();
623     if (nSelected >= 0)
624     {
625         OUString sString = maHelper.GetIntellectualPropertyPartNumbers()[nSelected];
626         m_xIntellectualPropertyPartEdit->replace_selection(sString);
627         m_xIntellectualPropertyPartEdit->grab_focus();
628     }
629     return true;
630 }
631 
IMPL_LINK(ClassificationDialog,SelectRecentlyUsedHdl,weld::ComboBox &,rBox,void)632 IMPL_LINK(ClassificationDialog, SelectRecentlyUsedHdl, weld::ComboBox&, rBox, void)
633 {
634     sal_Int32 nSelected = rBox.get_active();
635     if (nSelected >= 0)
636     {
637         m_xEditWindow->getEditEngine().Clear();
638         readIn(m_aRecentlyUsedValuesCollection[nSelected]);
639     }
640 }
641 
IMPL_LINK(ClassificationDialog,SelectIPPartHdl,weld::TreeView &,rBox,bool)642 IMPL_LINK(ClassificationDialog, SelectIPPartHdl, weld::TreeView&, rBox, bool)
643 {
644     const sal_Int32 nSelected = rBox.get_selected_index();
645     if (nSelected >= 0)
646     {
647         const OUString sString = maHelper.GetIntellectualPropertyParts()[nSelected];
648         m_xIntellectualPropertyPartEdit->replace_selection(sString);
649         m_xIntellectualPropertyPartEdit->grab_focus();
650     }
651     return true;
652 }
653 
IMPL_LINK(ClassificationDialog,ButtonClicked,weld::Button &,rButton,void)654 IMPL_LINK(ClassificationDialog, ButtonClicked, weld::Button&, rButton, void)
655 {
656     if (&rButton == m_xSignButton.get())
657     {
658         m_aParagraphSignHandler();
659     }
660     else if (&rButton == m_xIntellectualPropertyPartAddButton.get())
661     {
662         const OUString sString = m_xIntellectualPropertyPartEdit->get_text();
663         insertField(ClassificationType::INTELLECTUAL_PROPERTY_PART, sString, sString);
664     }
665 }
666 
IMPL_LINK_NOARG(ClassificationDialog,OkHdl,weld::Button &,void)667 IMPL_LINK_NOARG(ClassificationDialog, OkHdl, weld::Button&, void)
668 {
669     writeRecentlyUsed();
670     m_xDialog->response(RET_OK);
671 }
672 
IMPL_LINK_NOARG(ClassificationDialog,SelectToolboxHdl,weld::Toggleable &,void)673 IMPL_LINK_NOARG(ClassificationDialog, SelectToolboxHdl, weld::Toggleable&, void)
674 {
675     m_xEditWindow->InvertSelectionWeight();
676 }
677 
IMPL_LINK_NOARG(ClassificationDialog,EditWindowModifiedHdl,LinkParamNone *,void)678 IMPL_LINK_NOARG(ClassificationDialog, EditWindowModifiedHdl, LinkParamNone*, void)
679 {
680     toggleWidgetsDependingOnCategory();
681 }
682 
IMPL_STATIC_LINK(ClassificationDialog,ExpandedHdl,weld::Expander &,rExpander,void)683 IMPL_STATIC_LINK(ClassificationDialog, ExpandedHdl, weld::Expander&, rExpander, void)
684 {
685     std::shared_ptr<comphelper::ConfigurationChanges> aConfigurationChanges(comphelper::ConfigurationChanges::create());
686     officecfg::Office::Common::Classification::IntellectualPropertySectionExpanded::set(rExpander.get_expanded(), aConfigurationChanges);
687     aConfigurationChanges->commit();
688 }
689 
690 } // end svx
691 
692 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
693