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  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 
22 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <com/sun/star/container/XNameAccess.hpp>
25 #include <vcl/commandevent.hxx>
26 #include <vcl/commandinfoprovider.hxx>
27 #include <vcl/event.hxx>
28 #include <vcl/settings.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/weldutils.hxx>
31 #include <svl/intitem.hxx>
32 #include <svl/stritem.hxx>
33 #include <svl/style.hxx>
34 #include <comphelper/processfactory.hxx>
35 #include <comphelper/sequenceashashmap.hxx>
36 #include <com/sun/star/beans/PropertyValue.hpp>
37 #include <com/sun/star/frame/ModuleManager.hpp>
38 #include <com/sun/star/frame/UnknownModuleException.hpp>
39 #include <officecfg/Office/Common.hxx>
40 
41 #include <sal/log.hxx>
42 #include <osl/diagnose.h>
43 #include <tools/diagnose_ex.h>
44 #include <sfx2/app.hxx>
45 #include <sfx2/dispatch.hxx>
46 #include <sfx2/bindings.hxx>
47 #include <sfx2/templdlg.hxx>
48 #include <templdgi.hxx>
49 #include <tplcitem.hxx>
50 #include <sfx2/styfitem.hxx>
51 #include <sfx2/objsh.hxx>
52 #include <sfx2/viewsh.hxx>
53 #include <sfx2/newstyle.hxx>
54 #include <sfx2/tplpitem.hxx>
55 #include <sfx2/sfxresid.hxx>
56 
57 #include <sfx2/sfxsids.hrc>
58 #include <sfx2/strings.hrc>
59 #include <sfx2/docfac.hxx>
60 #include <sfx2/module.hxx>
61 #include <helpids.h>
62 #include <sfx2/viewfrm.hxx>
63 
64 #include <comphelper/string.hxx>
65 
66 #include <sfx2/StyleManager.hxx>
67 #include <sfx2/StylePreviewRenderer.hxx>
68 
69 using namespace css;
70 using namespace css::beans;
71 using namespace css::frame;
72 using namespace css::uno;
73 
74 class SfxCommonTemplateDialog_Impl::DeletionWatcher
75 {
76     typedef void (DeletionWatcher::* bool_type)();
77 
78 public:
DeletionWatcher(SfxCommonTemplateDialog_Impl & rDialog)79     explicit DeletionWatcher(SfxCommonTemplateDialog_Impl& rDialog)
80         : m_pDialog(&rDialog)
81         , m_pPrevious(m_pDialog->impl_setDeletionWatcher(this))
82     {
83     }
84 
~DeletionWatcher()85     ~DeletionWatcher()
86     {
87         if (m_pDialog)
88             m_pDialog->impl_setDeletionWatcher(m_pPrevious);
89     }
90 
91     DeletionWatcher(const DeletionWatcher&) = delete;
92     DeletionWatcher& operator=(const DeletionWatcher&) = delete;
93 
94     // Signal that the dialog was deleted
signal()95     void signal()
96     {
97         m_pDialog = nullptr;
98         if (m_pPrevious)
99             m_pPrevious->signal();
100     }
101 
102     // Return true if the dialog was deleted
operator bool_type() const103     operator bool_type() const
104     {
105         return m_pDialog ? nullptr : &DeletionWatcher::signal;
106     }
107 
108 private:
109     SfxCommonTemplateDialog_Impl* m_pDialog;
110     DeletionWatcher *const m_pPrevious; /// let's add more epicycles!
111 };
112 
113 /** Drop is enabled as long as it is allowed to create a new style by example, i.e. to
114     create a style out of the current selection.
115 */
AcceptDrop(const AcceptDropEvent & rEvt,const DropTargetHelper & rHelper)116 sal_Int8 SfxCommonTemplateDialog_Impl::AcceptDrop(const AcceptDropEvent& rEvt, const DropTargetHelper& rHelper)
117 {
118     if (rHelper.IsDropFormatSupported(SotClipboardFormatId::OBJECTDESCRIPTOR))
119     {
120         // special case: page styles are allowed to create new styles by example
121         // but not allowed to be created by drag and drop
122         if (GetActualFamily() == SfxStyleFamily::Page || bNewByExampleDisabled)
123             return DND_ACTION_NONE;
124         else
125             return DND_ACTION_COPY;
126     }
127 
128     // to enable the autoscroll when we're close to the edges
129     weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get();
130     pTreeView->get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
131     return DND_ACTION_MOVE;
132 }
133 
ExecuteDrop(const ExecuteDropEvent & rEvt)134 sal_Int8 SfxCommonTemplateDialog_Impl::ExecuteDrop(const ExecuteDropEvent& rEvt)
135 {
136     // handle drop of content into the treeview to create a new style
137     SfxObjectShell* pDocShell = GetObjectShell();
138     if (pDocShell)
139     {
140         TransferableDataHelper aHelper(rEvt.maDropEvent.Transferable);
141         sal_uInt32 nFormatCount = aHelper.GetFormatCount();
142 
143         sal_Int8 nRet = DND_ACTION_NONE;
144 
145         bool bFormatFound = false;
146 
147         for ( sal_uInt32 i = 0; i < nFormatCount; ++i )
148         {
149             SotClipboardFormatId nId = aHelper.GetFormat(i);
150             TransferableObjectDescriptor aDesc;
151 
152             if ( aHelper.GetTransferableObjectDescriptor( nId, aDesc ) )
153             {
154                 if ( aDesc.maClassName == pDocShell->GetFactory().GetClassId() )
155                 {
156                     Application::PostUserEvent(LINK(this, SfxCommonTemplateDialog_Impl, OnAsyncExecuteDrop));
157 
158                     bFormatFound = true;
159                     nRet =  rEvt.mnAction;
160                     break;
161                 }
162             }
163         }
164 
165         if (bFormatFound)
166             return nRet;
167     }
168 
169     if (!mxTreeBox->get_visible())
170         return DND_ACTION_NONE;
171 
172     if (!bAllowReParentDrop)
173         return DND_ACTION_NONE;
174 
175     // otherwise if we're dragging with the treeview to set a new parent of the dragged style
176     weld::TreeView* pSource = mxTreeBox->get_drag_source();
177     // only dragging within the same widget allowed
178     if (!pSource || pSource != mxTreeBox.get())
179         return DND_ACTION_NONE;
180 
181     std::unique_ptr<weld::TreeIter> xSource(mxTreeBox->make_iterator());
182     if (!mxTreeBox->get_selected(xSource.get()))
183         return DND_ACTION_NONE;
184 
185     std::unique_ptr<weld::TreeIter> xTarget(mxTreeBox->make_iterator());
186     if (!mxTreeBox->get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true))
187     {
188         // if nothing under the mouse, use the last row
189         int nChildren = mxTreeBox->n_children();
190         if (!nChildren)
191             return DND_ACTION_NONE;
192         if (!mxTreeBox->get_iter_first(*xTarget) || !mxTreeBox->iter_nth_sibling(*xTarget, nChildren - 1))
193             return DND_ACTION_NONE;
194         while (mxTreeBox->get_row_expanded(*xTarget))
195         {
196             nChildren = mxTreeBox->iter_n_children(*xTarget);
197             if (!mxTreeBox->iter_children(*xTarget) || !mxTreeBox->iter_nth_sibling(*xTarget, nChildren - 1))
198                 return DND_ACTION_NONE;
199         }
200     }
201     OUString aTargetStyle = mxTreeBox->get_text(*xTarget);
202     DropHdl(mxTreeBox->get_text(*xSource), aTargetStyle);
203     mxTreeBox->unset_drag_dest_row();
204     FillTreeBox();
205     SelectStyle(aTargetStyle, false);
206     return DND_ACTION_NONE;
207 }
208 
IMPL_LINK(SfxCommonTemplateDialog_Impl,DragBeginHdl,bool &,rUnsetDragIcon,bool)209 IMPL_LINK(SfxCommonTemplateDialog_Impl, DragBeginHdl, bool&, rUnsetDragIcon, bool)
210 {
211     rUnsetDragIcon = false;
212     // Allow normal processing. only if bAllowReParentDrop is true
213     return !bAllowReParentDrop;
214 }
215 
IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl,OnAsyncExecuteDrop,void *,void)216 IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, OnAsyncExecuteDrop, void*, void)
217 {
218     ActionSelect("new");
219 }
220 
IMPL_LINK(SfxCommonTemplateDialog_Impl,KeyInputHdl,const KeyEvent &,rKeyEvent,bool)221 IMPL_LINK(SfxCommonTemplateDialog_Impl, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
222 {
223     bool bRet = false;
224     const vcl::KeyCode& rKeyCode = rKeyEvent.GetKeyCode();
225     if (bCanDel && !rKeyCode.GetModifier() && rKeyCode.GetCode() == KEY_DELETE)
226     {
227         DeleteHdl();
228         bRet =  true;
229     }
230     return bRet;
231 }
232 
IMPL_LINK(SfxCommonTemplateDialog_Impl,QueryTooltipHdl,const weld::TreeIter &,rEntry,OUString)233 IMPL_LINK(SfxCommonTemplateDialog_Impl, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString)
234 {
235     weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get();
236     const OUString aTemplName(pTreeView->get_text(rEntry));
237     OUString sQuickHelpText(aTemplName);
238 
239     const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl();
240     if (!pItem)
241         return sQuickHelpText;
242     SfxStyleSheetBase* pStyle = pStyleSheetPool->Find(aTemplName, pItem->GetFamily());
243 
244     if (pStyle && pStyle->IsUsed())  // pStyle is in use in the document?
245     {
246         OUString sUsedBy;
247         if (pStyle->GetFamily() == SfxStyleFamily::Pseudo)
248             sUsedBy = pStyle->GetUsedBy();
249 
250         if (!sUsedBy.isEmpty())
251         {
252             const sal_Int32 nMaxLen = 80;
253             if (sUsedBy.getLength() > nMaxLen)
254             {
255                 sUsedBy = OUString::Concat(sUsedBy.subView(0, nMaxLen)) + "...";
256             }
257 
258             OUString aMessage = SfxResId(STR_STYLEUSEDBY);
259             aMessage = aMessage.replaceFirst("%STYLELIST", sUsedBy);
260             sQuickHelpText = aTemplName + " " + aMessage;
261         }
262     }
263 
264     return sQuickHelpText;
265 }
266 
IMPL_STATIC_LINK(SfxCommonTemplateDialog_Impl,CustomGetSizeHdl,weld::TreeView::get_size_args,aPayload,Size)267 IMPL_STATIC_LINK(SfxCommonTemplateDialog_Impl, CustomGetSizeHdl, weld::TreeView::get_size_args, aPayload, Size)
268 {
269     vcl::RenderContext& rRenderContext = aPayload.first;
270     return Size(42, 32 * rRenderContext.GetDPIScaleFactor());
271 }
272 
IMPL_LINK(SfxCommonTemplateDialog_Impl,CustomRenderHdl,weld::TreeView::render_args,aPayload,void)273 IMPL_LINK(SfxCommonTemplateDialog_Impl, CustomRenderHdl, weld::TreeView::render_args, aPayload, void)
274 {
275     vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
276     const ::tools::Rectangle& rRect = std::get<1>(aPayload);
277     ::tools::Rectangle aRect(rRect.TopLeft(), Size(rRenderContext.GetOutputSize().Width() - rRect.Left(), rRect.GetHeight()));
278     bool bSelected = std::get<2>(aPayload);
279     const OUString& rId = std::get<3>(aPayload);
280 
281     rRenderContext.Push(PushFlags::TEXTCOLOR);
282     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
283     if (bSelected)
284         rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
285     else
286         rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor());
287 
288     bool bSuccess = false;
289 
290     SfxObjectShell* pShell = SfxObjectShell::Current();
291     sfx2::StyleManager* pStyleManager = pShell ? pShell->GetStyleManager(): nullptr;
292 
293     if (pStyleManager)
294     {
295         const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl();
296         SfxStyleSheetBase* pStyleSheet = pStyleManager->Search(rId, pItem->GetFamily());
297 
298         if (pStyleSheet)
299         {
300             rRenderContext.Push(PushFlags::ALL);
301             sal_Int32 nSize = aRect.GetHeight();
302             std::unique_ptr<sfx2::StylePreviewRenderer> pStylePreviewRenderer(
303                 pStyleManager->CreateStylePreviewRenderer(rRenderContext, pStyleSheet, nSize));
304             bSuccess = pStylePreviewRenderer->recalculate() && pStylePreviewRenderer->render(aRect);
305             rRenderContext.Pop();
306         }
307     }
308 
309     if (!bSuccess)
310         rRenderContext.DrawText(aRect, rId, DrawTextFlags::Left | DrawTextFlags::VCenter);
311 
312     rRenderContext.Pop();
313 }
314 
IMPL_LINK(SfxCommonTemplateDialog_Impl,PopupFlatMenuHdl,const CommandEvent &,rCEvt,bool)315 IMPL_LINK(SfxCommonTemplateDialog_Impl, PopupFlatMenuHdl, const CommandEvent&, rCEvt, bool)
316 {
317     if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
318         return false;
319 
320     PrepareMenu(rCEvt.GetMousePosPixel());
321 
322     if (mxFmtLb->count_selected_rows() <= 0)
323     {
324         EnableEdit(false);
325         EnableDel(false);
326     }
327 
328     ShowMenu(rCEvt);
329 
330     return true;
331 }
332 
PrepareMenu(const Point & rPos)333 void SfxCommonTemplateDialog_Impl::PrepareMenu(const Point& rPos)
334 {
335     weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get();
336     std::unique_ptr<weld::TreeIter> xIter(pTreeView->make_iterator());
337     if (pTreeView->get_dest_row_at_pos(rPos, xIter.get(), false) && !pTreeView->is_selected(*xIter))
338     {
339         pTreeView->unselect_all();
340         pTreeView->set_cursor(*xIter);
341         pTreeView->select(*xIter);
342         FmtSelectHdl(*pTreeView);
343     }
344 }
345 
ShowMenu(const CommandEvent & rCEvt)346 void SfxCommonTemplateDialog_Impl::ShowMenu(const CommandEvent& rCEvt)
347 {
348     CreateContextMenu();
349 
350     weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get();
351     OString sCommand(mxMenu->popup_at_rect(pTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
352     MenuSelect(sCommand);
353 }
354 
IMPL_LINK(SfxCommonTemplateDialog_Impl,PopupTreeMenuHdl,const CommandEvent &,rCEvt,bool)355 IMPL_LINK(SfxCommonTemplateDialog_Impl, PopupTreeMenuHdl, const CommandEvent&, rCEvt, bool)
356 {
357     if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
358         return false;
359 
360     PrepareMenu(rCEvt.GetMousePosPixel());
361 
362     ShowMenu(rCEvt);
363 
364     return true;
365 }
366 
SfxTemplatePanelControl(SfxBindings * pBindings,weld::Widget * pParent)367 SfxTemplatePanelControl::SfxTemplatePanelControl(SfxBindings* pBindings, weld::Widget* pParent)
368     : PanelLayout(pParent, "TemplatePanel", "sfx/ui/templatepanel.ui")
369     , pImpl(new SfxTemplateDialog_Impl(pBindings, this))
370 {
371     OSL_ASSERT(pBindings!=nullptr);
372 }
373 
~SfxTemplatePanelControl()374 SfxTemplatePanelControl::~SfxTemplatePanelControl()
375 {
376 }
377 
MakeExpanded_Impl(const weld::TreeView & rBox,std::vector<OUString> & rEntries)378 static void MakeExpanded_Impl(const weld::TreeView& rBox, std::vector<OUString>& rEntries)
379 {
380     std::unique_ptr<weld::TreeIter> xEntry = rBox.make_iterator();
381     if (rBox.get_iter_first(*xEntry))
382     {
383         do
384         {
385             if (rBox.get_row_expanded(*xEntry))
386                 rEntries.push_back(rBox.get_text(*xEntry));
387         } while (rBox.iter_next(*xEntry));
388     }
389 }
390 
391 /** Internal structure for the establishment of the hierarchical view */
392 namespace {
393 
394 class StyleTree_Impl;
395 
396 }
397 
398 typedef std::vector<std::unique_ptr<StyleTree_Impl>> StyleTreeArr_Impl;
399 
400 namespace {
401 
402 class StyleTree_Impl
403 {
404 private:
405     OUString aName;
406     OUString aParent;
407     StyleTreeArr_Impl pChildren;
408 
409 public:
HasParent() const410     bool HasParent() const { return !aParent.isEmpty(); }
411 
StyleTree_Impl(const OUString & rName,const OUString & rParent)412     StyleTree_Impl(const OUString &rName, const OUString &rParent):
413         aName(rName), aParent(rParent), pChildren(0) {}
414 
getName() const415     const OUString& getName() const { return aName; }
getParent() const416     const OUString& getParent() const { return aParent; }
getChildren()417     StyleTreeArr_Impl& getChildren() { return pChildren; }
418 };
419 
420 }
421 
MakeTree_Impl(StyleTreeArr_Impl & rArr,const OUString & aUIName)422 static void MakeTree_Impl(StyleTreeArr_Impl& rArr, const OUString& aUIName)
423 {
424     const comphelper::string::NaturalStringSorter aSorter(
425         ::comphelper::getProcessComponentContext(),
426         Application::GetSettings().GetLanguageTag().getLocale());
427 
428     std::unordered_map<OUString, StyleTree_Impl*> styleFinder;
429     styleFinder.reserve(rArr.size());
430     for (const auto& pEntry : rArr)
431     {
432         styleFinder.emplace(pEntry->getName(), pEntry.get());
433     }
434 
435     // Arrange all under their Parents
436     for (auto& pEntry : rArr)
437     {
438         if (!pEntry->HasParent())
439             continue;
440         auto it = styleFinder.find(pEntry->getParent());
441         if (it != styleFinder.end())
442         {
443             StyleTree_Impl* pCmp = it->second;
444             // Insert child entries sorted
445             auto iPos = std::lower_bound(pCmp->getChildren().begin(), pCmp->getChildren().end(), pEntry,
446                 [&aSorter](std::unique_ptr<StyleTree_Impl> const & pEntry1, std::unique_ptr<StyleTree_Impl> const & pEntry2) { return aSorter.compare(pEntry1->getName(), pEntry2->getName()) < 0; });
447             pCmp->getChildren().insert(iPos, std::move(pEntry));
448         }
449     }
450 
451     // Only keep tree roots in rArr, child elements can be accessed through the hierarchy
452     rArr.erase(std::remove_if(rArr.begin(), rArr.end(), [](std::unique_ptr<StyleTree_Impl> const & pEntry) { return !pEntry; }), rArr.end());
453 
454     // tdf#91106 sort top level styles
455     std::sort(rArr.begin(), rArr.end());
456     std::sort(rArr.begin(), rArr.end(),
457         [&aSorter, &aUIName](std::unique_ptr<StyleTree_Impl> const & pEntry1, std::unique_ptr<StyleTree_Impl> const & pEntry2) {
458             if (pEntry2->getName() == aUIName)
459                 return false;
460             if (pEntry1->getName() == aUIName)
461                 return true; // default always first
462             return aSorter.compare(pEntry1->getName(), pEntry2->getName()) < 0;
463         });
464 }
465 
IsExpanded_Impl(const std::vector<OUString> & rEntries,std::u16string_view rStr)466 static bool IsExpanded_Impl( const std::vector<OUString>& rEntries,
467                              std::u16string_view rStr)
468 {
469     for (const auto & rEntry : rEntries)
470     {
471         if (rEntry == rStr)
472             return true;
473     }
474     return false;
475 }
476 
FillBox_Impl(weld::TreeView & rBox,StyleTree_Impl * pEntry,const std::vector<OUString> & rEntries,SfxStyleFamily eStyleFamily,const weld::TreeIter * pParent)477 static void FillBox_Impl(weld::TreeView& rBox,
478                          StyleTree_Impl* pEntry,
479                          const std::vector<OUString>& rEntries,
480                          SfxStyleFamily eStyleFamily,
481                          const weld::TreeIter* pParent)
482 {
483     std::unique_ptr<weld::TreeIter> xResult = rBox.make_iterator();
484     const OUString& rName = pEntry->getName();
485     rBox.insert(pParent, -1, &rName, &rName, nullptr, nullptr, false, xResult.get());
486 
487     for (size_t i = 0; i < pEntry->getChildren().size(); ++i)
488         FillBox_Impl(rBox, pEntry->getChildren()[i].get(), rEntries, eStyleFamily, xResult.get());
489 }
490 
491 namespace SfxTemplate
492 {
493     // converts from SFX_STYLE_FAMILY Ids to 1-6
SfxFamilyIdToNId(SfxStyleFamily nFamily)494     static sal_uInt16 SfxFamilyIdToNId(SfxStyleFamily nFamily)
495     {
496         switch ( nFamily )
497         {
498             case SfxStyleFamily::Char:   return 1;
499             case SfxStyleFamily::Para:   return 2;
500             case SfxStyleFamily::Frame:  return 3;
501             case SfxStyleFamily::Page:   return 4;
502             case SfxStyleFamily::Pseudo: return 5;
503             case SfxStyleFamily::Table:  return 6;
504             default:                     return 0xffff;
505         }
506     }
507 
508     // converts from 1-6 to SFX_STYLE_FAMILY Ids
NIdToSfxFamilyId(sal_uInt16 nId)509     static SfxStyleFamily NIdToSfxFamilyId(sal_uInt16 nId)
510     {
511         switch (nId)
512         {
513             case 1: return SfxStyleFamily::Char;
514             case 2: return SfxStyleFamily::Para;
515             case 3: return SfxStyleFamily::Frame;
516             case 4: return SfxStyleFamily::Page;
517             case 5: return SfxStyleFamily::Pseudo;
518             case 6: return SfxStyleFamily::Table;
519             default: return SfxStyleFamily::All;
520         }
521     }
522 }
523 
524 // Constructor
525 
SfxCommonTemplateDialog_Impl(SfxBindings * pB,weld::Container * pC,weld::Builder * pBuilder)526 SfxCommonTemplateDialog_Impl::SfxCommonTemplateDialog_Impl(SfxBindings* pB, weld::Container* pC, weld::Builder* pBuilder)
527     : pBindings(pB)
528     , mpContainer(pC)
529     , pModule(nullptr)
530     , pStyleSheetPool(nullptr)
531     , pCurObjShell(nullptr)
532     , xModuleManager(frame::ModuleManager::create(::comphelper::getProcessComponentContext()))
533     , m_pDeletionWatcher(nullptr)
534     , mxFmtLb(pBuilder->weld_tree_view("flatview"))
535     , mxTreeBox(pBuilder->weld_tree_view("treeview"))
536     , mxPreviewCheckbox(pBuilder->weld_check_button("showpreview"))
537     , mxFilterLb(pBuilder->weld_combo_box("filter"))
538 
539     , nActFamily(0xffff)
540     , nActFilter(0)
541     , nAppFilter(SfxStyleSearchBits::Auto)
542 
543     , m_nModifier(0)
544     , bDontUpdate(false)
545     , bIsWater(false)
546     , bUpdate(false)
547     , bUpdateFamily(false)
548     , bCanEdit(false)
549     , bCanDel(false)
550     , bCanNew(true)
551     , bCanHide(true)
552     , bCanShow(false)
553     , bWaterDisabled(false)
554     , bNewByExampleDisabled(false)
555     , bUpdateByExampleDisabled(false)
556     , bTreeDrag(true)
557     , bAllowReParentDrop(false)
558     , bHierarchical(false)
559     , m_bWantHierarchical(false)
560     , bBindingUpdate(true)
561 {
562     mxFmtLb->set_help_id(HID_TEMPLATE_FMT);
563     mxFilterLb->set_help_id(HID_TEMPLATE_FILTER);
564     mxPreviewCheckbox->set_active(officecfg::Office::Common::StylesAndFormatting::Preview::get());
565 }
566 
StyleNrToInfoOffset(sal_uInt16 nId)567 sal_uInt16 SfxCommonTemplateDialog_Impl::StyleNrToInfoOffset(sal_uInt16 nId)
568 {
569     const SfxStyleFamilyItem& rItem = mxStyleFamilies->at( nId );
570     return SfxTemplate::SfxFamilyIdToNId(rItem.GetFamily())-1;
571 }
572 
EnableEdit(bool bEnable)573 void SfxTemplateDialog_Impl::EnableEdit(bool bEnable)
574 {
575     SfxCommonTemplateDialog_Impl::EnableEdit( bEnable );
576     if( !bEnable || !bUpdateByExampleDisabled )
577         EnableItem("update", bEnable);
578 }
579 
ReadResource()580 void SfxCommonTemplateDialog_Impl::ReadResource()
581 {
582     // Read global user resource
583     for (auto & i : pFamilyState)
584         i.reset();
585 
586     SfxViewFrame* pViewFrame = pBindings->GetDispatcher_Impl()->GetFrame();
587     pCurObjShell = pViewFrame->GetObjectShell();
588     pModule = pCurObjShell ? pCurObjShell->GetModule() : nullptr;
589     if (pModule)
590         mxStyleFamilies = pModule->CreateStyleFamilies();
591     if (!mxStyleFamilies)
592         mxStyleFamilies.emplace();
593 
594     nActFilter = 0xffff;
595     if (pCurObjShell)
596     {
597         nActFilter = static_cast< sal_uInt16 >( LoadFactoryStyleFilter( pCurObjShell ) );
598         if ( 0xffff == nActFilter )
599             nActFilter = pCurObjShell->GetAutoStyleFilterIndex();
600     }
601 
602     // Paste in the toolbox
603     // reverse order, since always inserted at the head
604     size_t nCount = mxStyleFamilies->size();
605 
606     pBindings->ENTERREGISTRATIONS();
607 
608     size_t i;
609     for (i = 0; i < nCount; ++i)
610     {
611         sal_uInt16 nSlot = 0;
612         switch (mxStyleFamilies->at(i).GetFamily())
613         {
614             case SfxStyleFamily::Char:
615                 nSlot = SID_STYLE_FAMILY1; break;
616             case SfxStyleFamily::Para:
617                 nSlot = SID_STYLE_FAMILY2; break;
618             case SfxStyleFamily::Frame:
619                 nSlot = SID_STYLE_FAMILY3; break;
620             case SfxStyleFamily::Page:
621                 nSlot = SID_STYLE_FAMILY4; break;
622             case SfxStyleFamily::Pseudo:
623                 nSlot = SID_STYLE_FAMILY5; break;
624             case SfxStyleFamily::Table:
625                 nSlot = SID_STYLE_FAMILY6; break;
626             default: OSL_FAIL("unknown StyleFamily"); break;
627         }
628         pBoundItems[i].reset(
629             new SfxTemplateControllerItem(nSlot, *this, *pBindings) );
630     }
631     pBoundItems[i++].reset( new SfxTemplateControllerItem(
632         SID_STYLE_WATERCAN, *this, *pBindings) );
633     pBoundItems[i++].reset( new SfxTemplateControllerItem(
634         SID_STYLE_NEW_BY_EXAMPLE, *this, *pBindings) );
635     pBoundItems[i++].reset( new SfxTemplateControllerItem(
636         SID_STYLE_UPDATE_BY_EXAMPLE, *this, *pBindings) );
637     pBoundItems[i++].reset( new SfxTemplateControllerItem(
638         SID_STYLE_NEW, *this, *pBindings) );
639     pBoundItems[i++].reset( new SfxTemplateControllerItem(
640         SID_STYLE_DRAGHIERARCHIE, *this, *pBindings) );
641     pBoundItems[i++].reset( new SfxTemplateControllerItem(
642         SID_STYLE_EDIT, *this, *pBindings) );
643     pBoundItems[i++].reset( new SfxTemplateControllerItem(
644         SID_STYLE_DELETE, *this, *pBindings) );
645     pBoundItems[i++].reset( new SfxTemplateControllerItem(
646         SID_STYLE_FAMILY, *this, *pBindings) );
647     pBindings->LEAVEREGISTRATIONS();
648 
649     for(; i < COUNT_BOUND_FUNC; ++i)
650         pBoundItems[i] = nullptr;
651 
652     StartListening(*pBindings);
653 
654 // Insert in the reverse order of occurrence in the Style Families. This is for
655 // the toolbar of the designer. The list box of the catalog respects the
656 // correct order by itself.
657 
658 // Sequences: the order of Resource = the order of Toolbar for example list box.
659 // Order of ascending SIDs: Low SIDs are displayed first when templates of
660 // several families are active.
661 
662     // in the Writer the UpdateStyleByExample Toolbox button is removed and
663     // the NewStyle button gets a PopupMenu
664     if(nCount > 4)
665         ReplaceUpdateButtonByMenu();
666 
667     for( ; nCount--; )
668     {
669         const SfxStyleFamilyItem &rItem = mxStyleFamilies->at( nCount );
670         sal_uInt16 nId = SfxTemplate::SfxFamilyIdToNId( rItem.GetFamily() );
671         InsertFamilyItem(nId, rItem);
672     }
673 
674     for ( i = SID_STYLE_FAMILY1; i <= SID_STYLE_FAMILY4; i++ )
675         pBindings->Update(i);
676 }
677 
ClearResource()678 void SfxCommonTemplateDialog_Impl::ClearResource()
679 {
680     ClearFamilyList();
681     impl_clear();
682 }
683 
impl_clear()684 void SfxCommonTemplateDialog_Impl::impl_clear()
685 {
686     mxStyleFamilies.reset();
687     for (auto & i : pFamilyState)
688         i.reset();
689     for (auto & i : pBoundItems)
690         i.reset();
691     pCurObjShell = nullptr;
692 }
693 
694 SfxCommonTemplateDialog_Impl::DeletionWatcher *
impl_setDeletionWatcher(DeletionWatcher * const pNewWatcher)695 SfxCommonTemplateDialog_Impl::impl_setDeletionWatcher(
696         DeletionWatcher *const pNewWatcher)
697 {
698     DeletionWatcher *const pRet(m_pDeletionWatcher);
699     m_pDeletionWatcher = pNewWatcher;
700     return pRet;
701 }
702 
703 class TreeViewDropTarget final : public DropTargetHelper
704 {
705 private:
706     SfxCommonTemplateDialog_Impl& m_rParent;
707 
708 public:
TreeViewDropTarget(SfxCommonTemplateDialog_Impl & rDialog,weld::TreeView & rTreeView)709     TreeViewDropTarget(SfxCommonTemplateDialog_Impl& rDialog, weld::TreeView& rTreeView)
710         : DropTargetHelper(rTreeView.get_drop_target())
711         , m_rParent(rDialog)
712     {
713     }
714 
AcceptDrop(const AcceptDropEvent & rEvt)715     virtual sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt) override
716     {
717         return m_rParent.AcceptDrop(rEvt, *this);
718     }
719 
ExecuteDrop(const ExecuteDropEvent & rEvt)720     virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt) override
721     {
722         return m_rParent.ExecuteDrop(rEvt);
723     }
724 };
725 
Initialize()726 void SfxCommonTemplateDialog_Impl::Initialize()
727 {
728     // Read global user resource
729     ReadResource();
730     pBindings->Invalidate( SID_STYLE_FAMILY );
731     pBindings->Update( SID_STYLE_FAMILY );
732 
733     mxFilterLb->connect_changed(LINK(this, SfxCommonTemplateDialog_Impl, FilterSelectHdl));
734     mxFmtLb->connect_row_activated(LINK( this, SfxCommonTemplateDialog_Impl, TreeListApplyHdl));
735     mxFmtLb->connect_mouse_press(LINK(this, SfxCommonTemplateDialog_Impl, MousePressHdl));
736     mxFmtLb->connect_query_tooltip(LINK(this, SfxCommonTemplateDialog_Impl, QueryTooltipHdl));
737     mxFmtLb->connect_changed(LINK(this, SfxCommonTemplateDialog_Impl, FmtSelectHdl));
738     mxFmtLb->connect_popup_menu(LINK(this, SfxCommonTemplateDialog_Impl, PopupFlatMenuHdl));
739     mxFmtLb->connect_key_press(LINK(this, SfxCommonTemplateDialog_Impl, KeyInputHdl));
740     mxFmtLb->set_selection_mode(SelectionMode::Multiple);
741     mxTreeBox->connect_changed(LINK(this, SfxCommonTemplateDialog_Impl, FmtSelectHdl));
742     mxTreeBox->connect_row_activated(LINK( this, SfxCommonTemplateDialog_Impl, TreeListApplyHdl));
743     mxTreeBox->connect_mouse_press(LINK(this, SfxCommonTemplateDialog_Impl, MousePressHdl));
744     mxTreeBox->connect_query_tooltip(LINK(this, SfxCommonTemplateDialog_Impl, QueryTooltipHdl));
745     mxTreeBox->connect_popup_menu(LINK(this, SfxCommonTemplateDialog_Impl, PopupTreeMenuHdl));
746     mxTreeBox->connect_key_press(LINK(this, SfxCommonTemplateDialog_Impl, KeyInputHdl));
747     mxTreeBox->connect_drag_begin(LINK(this, SfxCommonTemplateDialog_Impl, DragBeginHdl));
748     mxPreviewCheckbox->connect_toggled(LINK(this, SfxCommonTemplateDialog_Impl, PreviewHdl));
749     m_xTreeView1DropTargetHelper.reset(new TreeViewDropTarget(*this, *mxFmtLb));
750     m_xTreeView2DropTargetHelper.reset(new TreeViewDropTarget(*this, *mxTreeBox));
751 
752     int nTreeHeight = mxFmtLb->get_height_rows(8);
753     mxFmtLb->set_size_request(-1, nTreeHeight);
754     mxTreeBox->set_size_request(-1, nTreeHeight);
755 
756     mxFmtLb->connect_custom_get_size(LINK(this, SfxCommonTemplateDialog_Impl, CustomGetSizeHdl));
757     mxFmtLb->connect_custom_render(LINK(this, SfxCommonTemplateDialog_Impl, CustomRenderHdl));
758     mxTreeBox->connect_custom_get_size(LINK(this, SfxCommonTemplateDialog_Impl, CustomGetSizeHdl));
759     mxTreeBox->connect_custom_render(LINK(this, SfxCommonTemplateDialog_Impl, CustomRenderHdl));
760     bool bCustomPreview = officecfg::Office::Common::StylesAndFormatting::Preview::get();
761     mxFmtLb->set_column_custom_renderer(0, bCustomPreview);
762     mxTreeBox->set_column_custom_renderer(0, bCustomPreview);
763 
764     mxFmtLb->set_visible(!bHierarchical);
765     mxTreeBox->set_visible(bHierarchical);
766 
767     Update_Impl();
768 }
769 
~SfxCommonTemplateDialog_Impl()770 SfxCommonTemplateDialog_Impl::~SfxCommonTemplateDialog_Impl()
771 {
772     if ( bIsWater )
773         Execute_Impl(SID_STYLE_WATERCAN, "", "", 0);
774     impl_clear();
775     if ( pStyleSheetPool )
776         EndListening(*pStyleSheetPool);
777     pStyleSheetPool = nullptr;
778     m_xTreeView1DropTargetHelper.reset();
779     m_xTreeView2DropTargetHelper.reset();
780     mxTreeBox.reset();
781     pIdle.reset();
782     if ( m_pDeletionWatcher )
783         m_pDeletionWatcher->signal();
784     mxFmtLb.reset();
785     mxPreviewCheckbox.reset();
786     mxFilterLb.reset();
787 }
788 
789 // Helper function: Access to the current family item
GetFamilyItem_Impl() const790 const SfxStyleFamilyItem *SfxCommonTemplateDialog_Impl::GetFamilyItem_Impl() const
791 {
792     const size_t nCount = mxStyleFamilies->size();
793     for(size_t i = 0; i < nCount; ++i)
794     {
795         const SfxStyleFamilyItem &rItem = mxStyleFamilies->at( i );
796         sal_uInt16 nId = SfxTemplate::SfxFamilyIdToNId(rItem.GetFamily());
797         if(nId == nActFamily)
798             return &rItem;
799     }
800     return nullptr;
801 }
802 
GetSelectedStyle() const803 void SfxCommonTemplateDialog_Impl::GetSelectedStyle() const
804 {
805     if (!IsInitialized() || !pStyleSheetPool || !HasSelectedStyle())
806         return;
807     const OUString aTemplName( GetSelectedEntry() );
808     const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl();
809     pStyleSheetPool->Find( aTemplName, pItem->GetFamily() );
810 }
811 
812 /**
813  * Is it safe to show the water-can / fill icon. If we've a
814  * hierarchical widget - we have only single select, otherwise
815  * we need to check if we have a multi-selection. We either have
816  * a mxTreeBox showing or an mxFmtLb (which we hide when not shown)
817  */
IsSafeForWaterCan() const818 bool SfxCommonTemplateDialog_Impl::IsSafeForWaterCan() const
819 {
820     if (mxTreeBox->get_visible())
821         return mxTreeBox->get_selected_index() != -1;
822     else
823         return mxFmtLb->count_selected_rows() == 1;
824 }
825 
SelectStyle(const OUString & rStr,bool bIsCallback)826 void SfxCommonTemplateDialog_Impl::SelectStyle(const OUString &rStr, bool bIsCallback)
827 {
828     const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl();
829     if ( !pItem )
830         return;
831     const SfxStyleFamily eFam = pItem->GetFamily();
832     SfxStyleSheetBase* pStyle = pStyleSheetPool->Find( rStr, eFam );
833     if( pStyle )
834     {
835         bool bReadWrite = !(pStyle->GetMask() & SfxStyleSearchBits::ReadOnly);
836         EnableEdit( bReadWrite );
837         EnableHide( bReadWrite && !pStyle->IsHidden( ) && !pStyle->IsUsed( ) );
838         EnableShow( bReadWrite && pStyle->IsHidden( ) );
839     }
840     else
841     {
842         EnableEdit(false);
843         EnableHide(false);
844         EnableShow(false);
845     }
846 
847     if (!bIsCallback)
848     {
849         if (mxTreeBox->get_visible())
850         {
851             if (!rStr.isEmpty())
852             {
853                 std::unique_ptr<weld::TreeIter> xEntry = mxTreeBox->make_iterator();
854                 bool bEntry = mxTreeBox->get_iter_first(*xEntry);
855                 while (bEntry)
856                 {
857                     if (mxTreeBox->get_text(*xEntry) == rStr)
858                     {
859                         mxTreeBox->scroll_to_row(*xEntry);
860                         mxTreeBox->select(*xEntry);
861                         break;
862                     }
863                     bEntry = mxTreeBox->iter_next(*xEntry);
864                 }
865             }
866             else if (eFam == SfxStyleFamily::Pseudo)
867             {
868                 std::unique_ptr<weld::TreeIter> xEntry = mxTreeBox->make_iterator();
869                 if (mxTreeBox->get_iter_first(*xEntry))
870                 {
871                     mxTreeBox->scroll_to_row(*xEntry);
872                     mxTreeBox->select(*xEntry);
873                 }
874             }
875             else
876                 mxTreeBox->unselect_all();
877         }
878         else
879         {
880             bool bSelect = !rStr.isEmpty();
881             if (bSelect)
882             {
883                 std::unique_ptr<weld::TreeIter> xEntry = mxFmtLb->make_iterator();
884                 bool bEntry = mxFmtLb->get_iter_first(*xEntry);
885                 while (bEntry && mxFmtLb->get_text(*xEntry) != rStr)
886                     bEntry = mxFmtLb->iter_next(*xEntry);
887                 if (!bEntry)
888                     bSelect = false;
889                 else
890                 {
891                     if (!mxFmtLb->is_selected(*xEntry))
892                     {
893                         mxFmtLb->unselect_all();
894                         mxFmtLb->scroll_to_row(*xEntry);
895                         mxFmtLb->select(*xEntry);
896                     }
897                 }
898             }
899 
900             if (!bSelect)
901             {
902                 mxFmtLb->unselect_all();
903                 EnableEdit(false);
904                 EnableHide(false);
905                 EnableShow(false);
906             }
907         }
908     }
909 
910     bWaterDisabled = !IsSafeForWaterCan();
911 
912     // tdf#134598 call UpdateStyleDependents to update watercan
913     UpdateStyleDependents();
914 }
915 
GetSelectedEntry() const916 OUString SfxCommonTemplateDialog_Impl::GetSelectedEntry() const
917 {
918     OUString aRet;
919     if (mxTreeBox->get_visible())
920         aRet = mxTreeBox->get_selected_text();
921     else
922         aRet = mxFmtLb->get_selected_text();
923     return aRet;
924 }
925 
EnableTreeDrag(bool bEnable)926 void SfxCommonTemplateDialog_Impl::EnableTreeDrag(bool bEnable)
927 {
928     if (pStyleSheetPool)
929     {
930         const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl();
931         SfxStyleSheetBase* pStyle = pItem ? pStyleSheetPool->First(pItem->GetFamily()) : nullptr;
932         bAllowReParentDrop = pStyle && pStyle->HasParentSupport() && bEnable;
933     }
934     bTreeDrag = bEnable;
935 }
936 
lcl_GetStyleFamilyName(SfxStyleFamily nFamily)937 static OUString lcl_GetStyleFamilyName( SfxStyleFamily nFamily )
938 {
939     if(nFamily == SfxStyleFamily::Char)
940         return "CharacterStyles" ;
941     if(nFamily == SfxStyleFamily::Para)
942         return "ParagraphStyles";
943     if(nFamily == SfxStyleFamily::Page)
944         return "PageStyles";
945     if(nFamily == SfxStyleFamily::Table)
946         return "TableStyles";
947     if (nFamily == SfxStyleFamily::Pseudo)
948         return "NumberingStyles";
949     return OUString();
950 }
951 
getDefaultStyleName(const SfxStyleFamily eFam)952 OUString SfxCommonTemplateDialog_Impl::getDefaultStyleName( const SfxStyleFamily eFam )
953 {
954     OUString sDefaultStyle;
955     OUString aFamilyName = lcl_GetStyleFamilyName(eFam);
956     if( aFamilyName == "TableStyles" )
957         sDefaultStyle = "Default Style";
958     else if(aFamilyName == "NumberingStyles")
959         sDefaultStyle = "No List";
960     else
961         sDefaultStyle = "Standard";
962     uno::Reference< style::XStyleFamiliesSupplier > xModel(GetObjectShell()->GetModel(), uno::UNO_QUERY);
963     OUString aUIName;
964     try
965     {
966         uno::Reference< container::XNameAccess > xStyles;
967         uno::Reference< container::XNameAccess > xCont = xModel->getStyleFamilies();
968         xCont->getByName( aFamilyName ) >>= xStyles;
969         uno::Reference< beans::XPropertySet > xInfo;
970         xStyles->getByName( sDefaultStyle ) >>= xInfo;
971         xInfo->getPropertyValue("DisplayName") >>= aUIName;
972     }
973     catch (const uno::Exception&)
974     {
975     }
976     return aUIName;
977 }
978 
FillTreeBox()979 void SfxCommonTemplateDialog_Impl::FillTreeBox()
980 {
981     assert(mxTreeBox && "FillTreeBox() without treebox");
982     if (!pStyleSheetPool || nActFamily == 0xffff)
983         return;
984 
985     const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl();
986     if (!pItem)
987         return;
988     const SfxStyleFamily eFam = pItem->GetFamily();
989     StyleTreeArr_Impl aArr;
990     SfxStyleSheetBase* pStyle = pStyleSheetPool->First(eFam, SfxStyleSearchBits::AllVisible);
991 
992     bAllowReParentDrop = pStyle && pStyle->HasParentSupport() && bTreeDrag;
993 
994     while (pStyle)
995     {
996         StyleTree_Impl* pNew = new StyleTree_Impl(pStyle->GetName(), pStyle->GetParent());
997         aArr.emplace_back(pNew);
998         pStyle = pStyleSheetPool->Next();
999     }
1000     OUString aUIName = getDefaultStyleName(eFam);
1001     MakeTree_Impl(aArr, aUIName);
1002     std::vector<OUString> aEntries;
1003     MakeExpanded_Impl(*mxTreeBox, aEntries);
1004     mxTreeBox->freeze();
1005     mxTreeBox->clear();
1006     const sal_uInt16 nCount = aArr.size();
1007 
1008     for (sal_uInt16 i = 0; i < nCount; ++i)
1009     {
1010         FillBox_Impl(*mxTreeBox, aArr[i].get(), aEntries, eFam, nullptr);
1011         aArr[i].reset();
1012     }
1013 
1014     EnableItem("watercan", false);
1015 
1016     SfxTemplateItem* pState = pFamilyState[nActFamily - 1].get();
1017 
1018     mxTreeBox->thaw();
1019 
1020     std::unique_ptr<weld::TreeIter> xEntry = mxTreeBox->make_iterator();
1021     bool bEntry = mxTreeBox->get_iter_first(*xEntry);
1022     if (bEntry && nCount)
1023         mxTreeBox->expand_row(*xEntry);
1024 
1025     while (bEntry)
1026     {
1027         if (IsExpanded_Impl(aEntries, mxTreeBox->get_text(*xEntry)))
1028             mxTreeBox->expand_row(*xEntry);
1029         bEntry = mxTreeBox->iter_next(*xEntry);
1030     }
1031 
1032     OUString aStyle;
1033     if(pState)  // Select current entry
1034         aStyle = pState->GetStyleName();
1035     SelectStyle(aStyle, false);
1036     EnableDelete();
1037 }
1038 
HasSelectedStyle() const1039 bool SfxCommonTemplateDialog_Impl::HasSelectedStyle() const
1040 {
1041     return mxTreeBox->get_visible() ? mxTreeBox->get_selected_index() != -1
1042                                     : mxFmtLb->count_selected_rows() != 0;
1043 }
1044 
1045 // internal: Refresh the display
1046 // nFlags: what we should update.
UpdateStyles_Impl(StyleFlags nFlags)1047 void SfxCommonTemplateDialog_Impl::UpdateStyles_Impl(StyleFlags nFlags)
1048 {
1049     OSL_ENSURE(nFlags != StyleFlags::NONE, "nothing to do");
1050     const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl();
1051     if (!pItem)
1052     {
1053         // Is the case for the template catalog
1054         const size_t nFamilyCount = mxStyleFamilies->size();
1055         size_t n;
1056         for( n = 0; n < nFamilyCount; n++ )
1057             if( pFamilyState[ StyleNrToInfoOffset(n) ] ) break;
1058         if ( n == nFamilyCount )
1059             // It happens sometimes, God knows why
1060             return;
1061         nAppFilter = pFamilyState[StyleNrToInfoOffset(n)]->GetValue();
1062         FamilySelect(  StyleNrToInfoOffset(n)+1 );
1063         pItem = GetFamilyItem_Impl();
1064     }
1065 
1066     const SfxStyleFamily eFam = pItem->GetFamily();
1067 
1068     SfxStyleSearchBits nFilter (nActFilter < pItem->GetFilterList().size() ? pItem->GetFilterList()[nActFilter].nFlags : SfxStyleSearchBits::Auto);
1069     if (nFilter == SfxStyleSearchBits::Auto)   // automatic
1070         nFilter = nAppFilter;
1071 
1072     OSL_ENSURE(pStyleSheetPool, "no StyleSheetPool");
1073     if(!pStyleSheetPool)
1074         return;
1075 
1076     pItem = GetFamilyItem_Impl();
1077     if(nFlags & StyleFlags::UpdateFamily)   // Update view type list (Hierarchical, All, etc.
1078     {
1079         CheckItem(OString::number(nActFamily));    // check Button in Toolbox
1080 
1081         mxFilterLb->freeze();
1082         mxFilterLb->clear();
1083 
1084         //insert hierarchical at the beginning
1085         mxFilterLb->append(OUString::number(static_cast<int>(SfxStyleSearchBits::All)), SfxResId(STR_STYLE_FILTER_HIERARCHICAL));
1086         const SfxStyleFilter& rFilter = pItem->GetFilterList();
1087         for (const SfxFilterTuple& i : rFilter)
1088             mxFilterLb->append(OUString::number(static_cast<int>(i.nFlags)), i.aName);
1089         mxFilterLb->thaw();
1090 
1091         if (nActFilter < mxFilterLb->get_count() - 1)
1092             mxFilterLb->set_active(nActFilter + 1);
1093         else
1094         {
1095             nActFilter = 0;
1096             mxFilterLb->set_active(1);
1097             nFilter = (nActFilter < rFilter.size()) ? rFilter[nActFilter].nFlags : SfxStyleSearchBits::Auto;
1098         }
1099 
1100         // if the tree view again, select family hierarchy
1101         if (mxTreeBox->get_visible() || m_bWantHierarchical)
1102         {
1103             mxFilterLb->set_active_text(SfxResId(STR_STYLE_FILTER_HIERARCHICAL));
1104             EnableHierarchical(true);
1105         }
1106     }
1107     else
1108     {
1109         if (nActFilter < mxFilterLb->get_count() - 1)
1110             mxFilterLb->set_active(nActFilter + 1);
1111         else
1112         {
1113             nActFilter = 0;
1114             mxFilterLb->set_active(1);
1115         }
1116     }
1117 
1118     if(!(nFlags & StyleFlags::UpdateFamilyList))
1119         return;
1120 
1121     EnableItem("watercan", false);
1122 
1123     SfxStyleSheetBase *pStyle = pStyleSheetPool->First(eFam, nFilter);
1124 
1125     std::unique_ptr<weld::TreeIter> xEntry = mxFmtLb->make_iterator();
1126     bool bEntry = mxFmtLb->get_iter_first(*xEntry);
1127     std::vector<OUString> aStrings;
1128 
1129     comphelper::string::NaturalStringSorter aSorter(
1130         ::comphelper::getProcessComponentContext(),
1131         Application::GetSettings().GetLanguageTag().getLocale());
1132 
1133     while( pStyle )
1134     {
1135         aStrings.push_back(pStyle->GetName());
1136         pStyle = pStyleSheetPool->Next();
1137     }
1138     OUString aUIName = getDefaultStyleName(eFam);
1139 
1140     // Paradoxically, with a list and non-Latin style names,
1141     // sorting twice is faster than sorting once.
1142     // The first sort has a cheap comparator, and gets the list into mostly-sorted order.
1143     // Then the second sort needs to call its (much more expensive) comparator less often.
1144     std::sort(aStrings.begin(), aStrings.end());
1145     std::sort(aStrings.begin(), aStrings.end(),
1146        [&aSorter, &aUIName](const OUString& rLHS, const OUString& rRHS) {
1147             if(rRHS == aUIName)
1148                 return false;
1149             if(rLHS == aUIName)
1150                 return true; // default always first
1151             return aSorter.compare(rLHS, rRHS) < 0;
1152        });
1153 
1154     size_t nCount = aStrings.size();
1155     size_t nPos = 0;
1156     while (nPos < nCount && bEntry &&
1157            aStrings[nPos] == mxFmtLb->get_text(*xEntry))
1158     {
1159         ++nPos;
1160         bEntry = mxFmtLb->iter_next(*xEntry);
1161     }
1162 
1163     if (nPos < nCount || bEntry)
1164     {
1165         // Fills the display box
1166         mxFmtLb->freeze();
1167         mxFmtLb->clear();
1168 
1169         for (nPos = 0; nPos < nCount; ++nPos)
1170             mxFmtLb->append(aStrings[nPos], aStrings[nPos]);
1171 
1172         mxFmtLb->thaw();
1173     }
1174     // Selects the current style if any
1175     SfxTemplateItem *pState = pFamilyState[nActFamily-1].get();
1176     OUString aStyle;
1177     if(pState)
1178         aStyle = pState->GetStyleName();
1179     SelectStyle(aStyle, false);
1180     EnableDelete();
1181 }
1182 
1183 // Updated display: Watering the house
SetWaterCanState(const SfxBoolItem * pItem)1184 void SfxCommonTemplateDialog_Impl::SetWaterCanState(const SfxBoolItem *pItem)
1185 {
1186     bWaterDisabled = (pItem == nullptr);
1187 
1188     if(!bWaterDisabled)
1189         //make sure the watercan is only activated when there is (only) one selection
1190         bWaterDisabled = !IsSafeForWaterCan();
1191 
1192     if(pItem && !bWaterDisabled)
1193     {
1194         CheckItem("watercan", pItem->GetValue());
1195         EnableItem("watercan");
1196     }
1197     else
1198     {
1199         if(!bWaterDisabled)
1200             EnableItem("watercan");
1201         else
1202             EnableItem("watercan", false);
1203     }
1204 
1205 // Ignore while in watercan mode statusupdates
1206 
1207     size_t nCount = mxStyleFamilies->size();
1208     pBindings->EnterRegistrations();
1209     for(size_t n = 0; n < nCount; n++)
1210     {
1211         SfxControllerItem *pCItem=pBoundItems[n].get();
1212         bool bChecked = pItem && pItem->GetValue();
1213         if( pCItem->IsBound() == bChecked )
1214         {
1215             if( !bChecked )
1216                 pCItem->ReBind();
1217             else
1218                 pCItem->UnBind();
1219         }
1220     }
1221     pBindings->LeaveRegistrations();
1222 }
1223 
1224 // Item with the status of a Family is copied and noted
1225 // (is updated when all states have also been updated.)
1226 // See also: <SfxBindings::AddDoneHdl(const Link &)>
SetFamilyState(sal_uInt16 nSlotId,const SfxTemplateItem * pItem)1227 void SfxCommonTemplateDialog_Impl::SetFamilyState( sal_uInt16 nSlotId, const SfxTemplateItem* pItem )
1228 {
1229     sal_uInt16 nIdx = nSlotId - SID_STYLE_FAMILY_START;
1230     pFamilyState[nIdx].reset();
1231     if ( pItem )
1232         pFamilyState[nIdx].reset( new SfxTemplateItem(*pItem) );
1233     bUpdate = true;
1234 
1235     // If used templates (how the hell you find this out??)
1236     bUpdateFamily = true;
1237 }
1238 
1239 // Notice from SfxBindings that the update is completed. Pushes out the update
1240 // of the display.
Update_Impl()1241 void SfxCommonTemplateDialog_Impl::Update_Impl()
1242 {
1243     bool bDocChanged=false;
1244     SfxStyleSheetBasePool* pNewPool = nullptr;
1245     SfxViewFrame* pViewFrame = pBindings->GetDispatcher_Impl()->GetFrame();
1246     SfxObjectShell* pDocShell = pViewFrame->GetObjectShell();
1247     if( pDocShell )
1248         pNewPool = pDocShell->GetStyleSheetPool();
1249 
1250     if ( pNewPool != pStyleSheetPool && pDocShell )
1251     {
1252         SfxModule* pNewModule = pDocShell->GetModule();
1253         if( pNewModule && pNewModule != pModule )
1254         {
1255             ClearResource();
1256             ReadResource();
1257         }
1258         if ( pStyleSheetPool )
1259         {
1260             EndListening(*pStyleSheetPool);
1261             pStyleSheetPool = nullptr;
1262         }
1263 
1264         if ( pNewPool )
1265         {
1266             StartListening(*pNewPool);
1267             pStyleSheetPool = pNewPool;
1268             bDocChanged=true;
1269         }
1270     }
1271 
1272     if (bUpdateFamily)
1273         UpdateFamily_Impl();
1274 
1275     sal_uInt16 i;
1276     for(i = 0; i < MAX_FAMILIES; ++i)
1277         if(pFamilyState[i])
1278             break;
1279     if(i == MAX_FAMILIES || !pNewPool)
1280         // nothing is allowed
1281         return;
1282 
1283     SfxTemplateItem *pItem = nullptr;
1284     // current region not within the allowed region or default
1285     if(nActFamily == 0xffff || nullptr == (pItem = pFamilyState[nActFamily-1].get() ) )
1286     {
1287          CheckItem(OString::number(nActFamily), false);
1288          const size_t nFamilyCount = mxStyleFamilies->size();
1289          size_t n;
1290          for( n = 0; n < nFamilyCount; n++ )
1291              if( pFamilyState[ StyleNrToInfoOffset(n) ] ) break;
1292 
1293          std::unique_ptr<SfxTemplateItem> & pNewItem = pFamilyState[StyleNrToInfoOffset(n)];
1294          nAppFilter = pNewItem->GetValue();
1295          FamilySelect( StyleNrToInfoOffset(n) + 1 );
1296          pItem = pNewItem.get();
1297     }
1298     else if( bDocChanged )
1299     {
1300          // other DocShell -> all new
1301          CheckItem(OString::number(nActFamily));
1302          nActFilter = static_cast< sal_uInt16 >( LoadFactoryStyleFilter( pDocShell ) );
1303          if ( 0xffff == nActFilter )
1304             nActFilter = pDocShell->GetAutoStyleFilterIndex();
1305 
1306          nAppFilter = pItem->GetValue();
1307          if (!mxTreeBox->get_visible())
1308          {
1309              UpdateStyles_Impl(StyleFlags::UpdateFamilyList);
1310          }
1311          else
1312              FillTreeBox();
1313     }
1314     else
1315     {
1316          // other filters for automatic
1317          CheckItem(OString::number(nActFamily));
1318          const SfxStyleFamilyItem *pStyleItem =  GetFamilyItem_Impl();
1319          if ( pStyleItem && SfxStyleSearchBits::Auto == pStyleItem->GetFilterList()[nActFilter].nFlags
1320             && nAppFilter != pItem->GetValue())
1321          {
1322              nAppFilter = pItem->GetValue();
1323              if (!mxTreeBox->get_visible())
1324                  UpdateStyles_Impl(StyleFlags::UpdateFamilyList);
1325              else
1326                  FillTreeBox();
1327          }
1328          else
1329              nAppFilter = pItem->GetValue();
1330     }
1331     const OUString aStyle(pItem->GetStyleName());
1332     SelectStyle(aStyle, false);
1333     EnableDelete();
1334     EnableNew( bCanNew );
1335 }
1336 
IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl,TimeOut,Timer *,void)1337 IMPL_LINK_NOARG( SfxCommonTemplateDialog_Impl, TimeOut, Timer *, void )
1338 {
1339     if(!bDontUpdate)
1340     {
1341         bDontUpdate=true;
1342         if (!mxTreeBox->get_visible())
1343             UpdateStyles_Impl(StyleFlags::UpdateFamilyList);
1344         else
1345         {
1346             FillTreeBox();
1347             SfxTemplateItem *pState = pFamilyState[nActFamily-1].get();
1348             if(pState)
1349             {
1350                 SelectStyle(pState->GetStyleName(), false);
1351                 EnableDelete();
1352             }
1353         }
1354         bDontUpdate=false;
1355         pIdle.reset();
1356     }
1357     else
1358         pIdle->Start();
1359 }
1360 
Notify(SfxBroadcaster &,const SfxHint & rHint)1361 void SfxCommonTemplateDialog_Impl::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
1362 {
1363     const SfxHintId nId = rHint.GetId();
1364 
1365     // tap update
1366     switch(nId)
1367     {
1368         case SfxHintId::UpdateDone:
1369             {
1370                 SfxViewFrame *pViewFrame = pBindings->GetDispatcher_Impl()->GetFrame();
1371                 SfxObjectShell *pDocShell = pViewFrame->GetObjectShell();
1372                 if (
1373                         bUpdate &&
1374                         (
1375                          !IsCheckedItem("watercan") ||
1376                          (pDocShell && pDocShell->GetStyleSheetPool() != pStyleSheetPool)
1377                         )
1378                    )
1379                 {
1380                     bUpdate = false;
1381                     Update_Impl();
1382                 }
1383                 else if ( bUpdateFamily )
1384                 {
1385                     UpdateFamily_Impl();
1386                 }
1387 
1388                 if( pStyleSheetPool )
1389                 {
1390                     OUString aStr = GetSelectedEntry();
1391                     if (!aStr.isEmpty())
1392                     {
1393                         const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl();
1394                         if( !pItem ) break;
1395                         const SfxStyleFamily eFam = pItem->GetFamily();
1396                         SfxStyleSheetBase *pStyle = pStyleSheetPool->Find( aStr, eFam );
1397                         if( pStyle )
1398                         {
1399                             bool bReadWrite = !(pStyle->GetMask() & SfxStyleSearchBits::ReadOnly);
1400                             EnableEdit( bReadWrite );
1401                             EnableHide( bReadWrite && !pStyle->IsUsed( ) && !pStyle->IsHidden( ) );
1402                             EnableShow( bReadWrite && pStyle->IsHidden( ) );
1403                         }
1404                         else
1405                         {
1406                             EnableEdit(false);
1407                             EnableHide(false);
1408                             EnableShow(false);
1409                         }
1410                     }
1411                 }
1412                 break;
1413             }
1414 
1415             // Necessary if switching between documents and in both documents
1416             // the same template is used. Do not immediately call Update_Impl,
1417             // for the case that one of the documents is an internal InPlaceObject!
1418         case SfxHintId::DocChanged:
1419             bUpdate = true;
1420         break;
1421         case SfxHintId::Dying:
1422             {
1423                 EndListening(*pStyleSheetPool);
1424                 pStyleSheetPool=nullptr;
1425                 break;
1426             }
1427         default: break;
1428     }
1429 
1430     // Do not set timer when the stylesheet pool is in the box, because it is
1431     // possible that a new one is registered after the timer is up -
1432     // works bad in UpdateStyles_Impl ()!
1433 
1434     if(!bDontUpdate && nId != SfxHintId::Dying &&
1435        (dynamic_cast<const SfxStyleSheetPoolHint*>(&rHint) ||
1436         dynamic_cast<const SfxStyleSheetHint*>(&rHint) ||
1437         dynamic_cast<const SfxStyleSheetModifiedHint*>(&rHint) ||
1438         nId == SfxHintId::StyleSheetModified))
1439     {
1440         if(!pIdle)
1441         {
1442             pIdle.reset(new Idle("SfxCommonTemplate"));
1443             pIdle->SetPriority(TaskPriority::LOWEST);
1444             pIdle->SetInvokeHandler(LINK(this,SfxCommonTemplateDialog_Impl,TimeOut));
1445         }
1446         pIdle->Start();
1447 
1448     }
1449 }
1450 
1451 // Other filters; can be switched by the users or as a result of new or
1452 // editing, if the current document has been assigned a different filter.
FilterSelect(sal_uInt16 nEntry,bool bForce)1453 void SfxCommonTemplateDialog_Impl::FilterSelect(
1454                 sal_uInt16 nEntry,  // Idx of the new Filters
1455                 bool bForce )   // Force update, even if the new filter is
1456                                 // equal to the current
1457 {
1458     if( nEntry == nActFilter && !bForce )
1459         return;
1460 
1461     nActFilter = nEntry;
1462     SfxObjectShell *const pDocShell = SaveSelection();
1463     SfxStyleSheetBasePool *pOldStyleSheetPool = pStyleSheetPool;
1464     pStyleSheetPool = pDocShell? pDocShell->GetStyleSheetPool(): nullptr;
1465     if ( pOldStyleSheetPool != pStyleSheetPool )
1466     {
1467         if ( pOldStyleSheetPool )
1468             EndListening(*pOldStyleSheetPool);
1469         if ( pStyleSheetPool )
1470             StartListening(*pStyleSheetPool);
1471     }
1472 
1473     UpdateStyles_Impl(StyleFlags::UpdateFamilyList);
1474 }
1475 
1476 // Internal: Perform functions through the Dispatcher
Execute_Impl(sal_uInt16 nId,const OUString & rStr,const OUString & rRefStr,sal_uInt16 nFamily,SfxStyleSearchBits nMask,sal_uInt16 * pIdx,const sal_uInt16 * pModifier)1477 bool SfxCommonTemplateDialog_Impl::Execute_Impl(
1478     sal_uInt16 nId, const OUString &rStr, const OUString& rRefStr, sal_uInt16 nFamily,
1479     SfxStyleSearchBits nMask, sal_uInt16 *pIdx, const sal_uInt16* pModifier)
1480 {
1481     SfxDispatcher &rDispatcher = *SfxGetpApp()->GetDispatcher_Impl();
1482     SfxStringItem aItem(nId, rStr);
1483     SfxUInt16Item aFamily(SID_STYLE_FAMILY, nFamily);
1484     SfxUInt16Item aMask( SID_STYLE_MASK, static_cast<sal_uInt16>(nMask) );
1485     SfxStringItem aUpdName(SID_STYLE_UPD_BY_EX_NAME, rStr);
1486     SfxStringItem aRefName( SID_STYLE_REFERENCE, rRefStr );
1487     const SfxPoolItem* pItems[ 6 ];
1488     sal_uInt16 nCount = 0;
1489     if( !rStr.isEmpty() )
1490         pItems[ nCount++ ] = &aItem;
1491     pItems[ nCount++ ] = &aFamily;
1492     if( nMask != SfxStyleSearchBits::Auto )
1493         pItems[ nCount++ ] = &aMask;
1494     if(SID_STYLE_UPDATE_BY_EXAMPLE == nId)
1495     {
1496         // Special solution for Numbering update in Writer
1497         const OUString aTemplName(GetSelectedEntry());
1498         aUpdName.SetValue(aTemplName);
1499         pItems[ nCount++ ] = &aUpdName;
1500     }
1501 
1502     if ( !rRefStr.isEmpty() )
1503         pItems[ nCount++ ] = &aRefName;
1504 
1505     pItems[ nCount++ ] = nullptr;
1506 
1507     DeletionWatcher aDeleted(*this);
1508     sal_uInt16 nModi = pModifier ? *pModifier : 0;
1509     const SfxPoolItem* pItem = rDispatcher.Execute(
1510         nId, SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
1511         pItems, nModi );
1512 
1513     // Dialog can be destroyed while in Execute() because started
1514     // subdialogs are not modal to it (#i97888#).
1515     if ( !pItem || aDeleted )
1516         return false;
1517 
1518     if ( (nId == SID_STYLE_NEW || SID_STYLE_EDIT == nId) && (mxTreeBox->get_visible() || mxFmtLb->count_selected_rows() <= 1) )
1519     {
1520         const SfxUInt16Item *pFilterItem = dynamic_cast< const SfxUInt16Item* >(pItem);
1521         assert(pFilterItem);
1522         SfxStyleSearchBits nFilterFlags = static_cast<SfxStyleSearchBits>(pFilterItem->GetValue()) & ~SfxStyleSearchBits::UserDefined;
1523         if(nFilterFlags == SfxStyleSearchBits::Auto)       // User Template?
1524             nFilterFlags = static_cast<SfxStyleSearchBits>(pFilterItem->GetValue());
1525         const SfxStyleFamilyItem *pFamilyItem = GetFamilyItem_Impl();
1526         const size_t nFilterCount = pFamilyItem->GetFilterList().size();
1527 
1528         for ( size_t i = 0; i < nFilterCount; ++i )
1529         {
1530             const SfxFilterTuple &rTupel = pFamilyItem->GetFilterList()[ i ];
1531 
1532             if ( ( rTupel.nFlags & nFilterFlags ) == nFilterFlags && pIdx )
1533                 *pIdx = i;
1534         }
1535     }
1536 
1537     return true;
1538 }
1539 
1540 // Handler Listbox of Filter
EnableHierarchical(bool const bEnable)1541 void SfxCommonTemplateDialog_Impl::EnableHierarchical(bool const bEnable)
1542 {
1543     if (bEnable)
1544     {
1545         if (!bHierarchical)
1546         {
1547             // Turn on treeView
1548             bHierarchical=true;
1549             m_bWantHierarchical = true;
1550             SaveSelection(); // fdo#61429 store "hierarchical"
1551             const OUString aSelectEntry( GetSelectedEntry());
1552             mxFmtLb->hide();
1553             FillTreeBox();
1554             SelectStyle(aSelectEntry, false);
1555             mxTreeBox->show();
1556         }
1557     }
1558     else
1559     {
1560         mxTreeBox->hide();
1561         mxFmtLb->show();
1562         // If bHierarchical, then the family can have changed
1563         // minus one since hierarchical is inserted at the start
1564         m_bWantHierarchical = false; // before FilterSelect
1565         FilterSelect(mxFilterLb->get_active() - 1, bHierarchical );
1566         bHierarchical=false;
1567     }
1568 }
1569 
IMPL_LINK(SfxCommonTemplateDialog_Impl,FilterSelectHdl,weld::ComboBox &,rBox,void)1570 IMPL_LINK(SfxCommonTemplateDialog_Impl, FilterSelectHdl, weld::ComboBox&, rBox, void)
1571 {
1572     if (SfxResId(STR_STYLE_FILTER_HIERARCHICAL) == rBox.get_active_text())
1573     {
1574         EnableHierarchical(true);
1575     }
1576     else
1577     {
1578         EnableHierarchical(false);
1579     }
1580 }
1581 
1582 // Select-Handler for the Toolbox
FamilySelect(sal_uInt16 nEntry,bool bPreviewRefresh)1583 void SfxCommonTemplateDialog_Impl::FamilySelect(sal_uInt16 nEntry, bool bPreviewRefresh)
1584 {
1585     assert((0 < nEntry && nEntry <= MAX_FAMILIES) || 0xffff == nEntry);
1586     if( nEntry != nActFamily || bPreviewRefresh )
1587     {
1588         CheckItem(OString::number(nActFamily), false);
1589         nActFamily = nEntry;
1590         SfxDispatcher* pDispat = pBindings->GetDispatcher_Impl();
1591         SfxUInt16Item const aItem(SID_STYLE_FAMILY,
1592                 static_cast<sal_uInt16>(SfxTemplate::NIdToSfxFamilyId(nEntry)));
1593         pDispat->ExecuteList(SID_STYLE_FAMILY, SfxCallMode::SYNCHRON, { &aItem });
1594         pBindings->Invalidate( SID_STYLE_FAMILY );
1595         pBindings->Update( SID_STYLE_FAMILY );
1596         UpdateFamily_Impl();
1597     }
1598 }
1599 
ActionSelect(const OString & rEntry)1600 void SfxCommonTemplateDialog_Impl::ActionSelect(const OString& rEntry)
1601 {
1602     if (rEntry == "watercan")
1603     {
1604         const bool bOldState = !IsCheckedItem(rEntry);
1605         bool bCheck;
1606         SfxBoolItem aBool;
1607         // when a template is chosen.
1608         if (!bOldState && HasSelectedStyle())
1609         {
1610             const OUString aTemplName(
1611                 GetSelectedEntry());
1612             Execute_Impl(
1613                 SID_STYLE_WATERCAN, aTemplName, "",
1614                 static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()) );
1615             bCheck = true;
1616         }
1617         else
1618         {
1619             Execute_Impl(SID_STYLE_WATERCAN, "", "", 0);
1620             bCheck = false;
1621         }
1622         CheckItem(rEntry, bCheck);
1623         aBool.SetValue(bCheck);
1624         SetWaterCanState(&aBool);
1625     }
1626     else if (rEntry == "new" || rEntry == "newmenu")
1627     {
1628         if(pStyleSheetPool && nActFamily != 0xffff)
1629         {
1630             const SfxStyleFamily eFam=GetFamilyItem_Impl()->GetFamily();
1631             const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl();
1632             SfxStyleSearchBits nFilter(SfxStyleSearchBits::Auto);
1633             if (pItem && nActFilter != 0xffff)
1634                 nFilter = pItem->GetFilterList()[nActFilter].nFlags;
1635             if (nFilter == SfxStyleSearchBits::Auto)    // automatic
1636                 nFilter = nAppFilter;
1637 
1638             // why? : FloatingWindow must not be parent of a modal dialog
1639             SfxNewStyleDlg aDlg(mpContainer, *pStyleSheetPool, eFam);
1640             auto nResult = aDlg.run();
1641             if (nResult ==  RET_OK)
1642             {
1643                 const OUString aTemplName(aDlg.GetName());
1644                 Execute_Impl(SID_STYLE_NEW_BY_EXAMPLE,
1645                              aTemplName, "",
1646                              static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()),
1647                              nFilter);
1648                 UpdateFamily_Impl();
1649             }
1650         }
1651     }
1652     else if (rEntry == "update")
1653     {
1654         Execute_Impl(SID_STYLE_UPDATE_BY_EXAMPLE,
1655                 "", "",
1656                 static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()));
1657     }
1658     else if (rEntry == "load")
1659         SfxGetpApp()->GetDispatcher_Impl()->Execute(SID_TEMPLATE_LOAD);
1660     else
1661         SAL_WARN("sfx", "not implemented: " << rEntry);
1662 }
1663 
getModuleIdentifier(const Reference<XModuleManager2> & i_xModMgr,SfxObjectShell const * i_pObjSh)1664 static OUString getModuleIdentifier( const Reference< XModuleManager2 >& i_xModMgr, SfxObjectShell const * i_pObjSh )
1665 {
1666     OSL_ENSURE( i_xModMgr.is(), "getModuleIdentifier(): no XModuleManager" );
1667     OSL_ENSURE( i_pObjSh, "getModuleIdentifier(): no ObjectShell" );
1668 
1669     OUString sIdentifier;
1670 
1671     try
1672     {
1673         sIdentifier = i_xModMgr->identify( i_pObjSh->GetModel() );
1674     }
1675     catch ( css::frame::UnknownModuleException& )
1676     {
1677         SAL_WARN("sfx", "getModuleIdentifier(): unknown module" );
1678     }
1679     catch ( Exception& )
1680     {
1681         TOOLS_WARN_EXCEPTION( "sfx", "getModuleIdentifier(): exception of XModuleManager::identify()" );
1682     }
1683 
1684     return sIdentifier;
1685 }
1686 
LoadFactoryStyleFilter(SfxObjectShell const * i_pObjSh)1687 sal_Int32 SfxCommonTemplateDialog_Impl::LoadFactoryStyleFilter( SfxObjectShell const * i_pObjSh )
1688 {
1689     OSL_ENSURE( i_pObjSh, "SfxCommonTemplateDialog_Impl::LoadFactoryStyleFilter(): no ObjectShell" );
1690 
1691     ::comphelper::SequenceAsHashMap aFactoryProps(
1692         xModuleManager->getByName( getModuleIdentifier( xModuleManager, i_pObjSh ) ) );
1693     sal_Int32 nFilter = aFactoryProps.getUnpackedValueOrDefault( "ooSetupFactoryStyleFilter", sal_Int32(-1) );
1694 
1695     m_bWantHierarchical = (nFilter & 0x1000) != 0;
1696     nFilter &= ~0x1000; // clear it
1697 
1698     return nFilter;
1699 }
1700 
SaveFactoryStyleFilter(SfxObjectShell const * i_pObjSh,sal_Int32 i_nFilter)1701 void SfxCommonTemplateDialog_Impl::SaveFactoryStyleFilter( SfxObjectShell const * i_pObjSh, sal_Int32 i_nFilter )
1702 {
1703     OSL_ENSURE( i_pObjSh, "SfxCommonTemplateDialog_Impl::LoadFactoryStyleFilter(): no ObjectShell" );
1704     Sequence< PropertyValue > lProps(1);
1705     lProps[0].Name = "ooSetupFactoryStyleFilter";
1706     lProps[0].Value <<= i_nFilter | (m_bWantHierarchical ? 0x1000 : 0);
1707     xModuleManager->replaceByName( getModuleIdentifier( xModuleManager, i_pObjSh ), makeAny( lProps ) );
1708 }
1709 
SaveSelection()1710 SfxObjectShell* SfxCommonTemplateDialog_Impl::SaveSelection()
1711 {
1712     SfxViewFrame *const pViewFrame(pBindings->GetDispatcher_Impl()->GetFrame());
1713     SfxObjectShell *const pDocShell(pViewFrame->GetObjectShell());
1714     if (pDocShell)
1715     {
1716         pDocShell->SetAutoStyleFilterIndex(nActFilter);
1717         SaveFactoryStyleFilter( pDocShell, nActFilter );
1718     }
1719     return pDocShell;
1720 }
1721 
DropHdl(const OUString & rStyle,const OUString & rParent)1722 void SfxCommonTemplateDialog_Impl::DropHdl(const OUString& rStyle, const OUString& rParent)
1723 {
1724     bDontUpdate = true;
1725     const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl();
1726     const SfxStyleFamily eFam = pItem->GetFamily();
1727     pStyleSheetPool->SetParent(eFam, rStyle, rParent);
1728     bDontUpdate = false;
1729 }
1730 
1731 // Handler for the New-Buttons
NewHdl()1732 void SfxCommonTemplateDialog_Impl::NewHdl()
1733 {
1734     if ( nActFamily == 0xffff || !(mxTreeBox->get_visible() || mxFmtLb->count_selected_rows() <= 1))
1735         return;
1736 
1737     const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl();
1738     const SfxStyleFamily eFam = pItem->GetFamily();
1739     SfxStyleSearchBits nMask(SfxStyleSearchBits::Auto);
1740     if (nActFilter != 0xffff)
1741         nMask = pItem->GetFilterList()[nActFilter].nFlags;
1742     if (nMask == SfxStyleSearchBits::Auto)    // automatic
1743         nMask = nAppFilter;
1744 
1745     Execute_Impl(SID_STYLE_NEW,
1746                  "", GetSelectedEntry(),
1747                  static_cast<sal_uInt16>(eFam),
1748                  nMask);
1749 }
1750 
1751 // Handler for the edit-Buttons
EditHdl()1752 void SfxCommonTemplateDialog_Impl::EditHdl()
1753 {
1754     if(IsInitialized() && HasSelectedStyle())
1755     {
1756         sal_uInt16 nFilter = nActFilter;
1757         OUString aTemplName(GetSelectedEntry());
1758         GetSelectedStyle(); // -Wall required??
1759         Execute_Impl( SID_STYLE_EDIT, aTemplName, OUString(),
1760                           static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()), SfxStyleSearchBits::Auto, &nFilter );
1761     }
1762 }
1763 
1764 // Handler for the Delete-Buttons
DeleteHdl()1765 void SfxCommonTemplateDialog_Impl::DeleteHdl()
1766 {
1767     if ( !IsInitialized() || !HasSelectedStyle() )
1768         return;
1769 
1770     bool bUsedStyle = false;     // one of the selected styles are used in the document?
1771 
1772     std::vector<std::unique_ptr<weld::TreeIter>> aList;
1773     weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get();
1774     const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl();
1775 
1776     OUStringBuffer aMsg;
1777     aMsg.append(SfxResId(STR_DELETE_STYLE_USED) + SfxResId(STR_DELETE_STYLE));
1778 
1779     pTreeView->selected_foreach([this, pTreeView, pItem, &aList, &bUsedStyle, &aMsg](weld::TreeIter& rEntry){
1780         aList.emplace_back(pTreeView->make_iterator(&rEntry));
1781         // check the style is used or not
1782         const OUString aTemplName(pTreeView->get_text(rEntry));
1783 
1784         SfxStyleSheetBase* pStyle = pStyleSheetPool->Find( aTemplName, pItem->GetFamily() );
1785 
1786         if ( pStyle->IsUsed() )  // pStyle is in use in the document?
1787         {
1788             if (bUsedStyle) // add a separator for the second and later styles
1789                 aMsg.append(", ");
1790             aMsg.append(aTemplName);
1791             bUsedStyle = true;
1792         }
1793 
1794         return false;
1795     });
1796 
1797     bool aApproved = false;
1798 
1799     // we only want to show the dialog once and if we want to delete a style in use (UX-advice)
1800     if (bUsedStyle)
1801     {
1802         std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pTreeView,
1803                                                   VclMessageType::Question, VclButtonsType::YesNo,
1804                                                   aMsg.makeStringAndClear()));
1805         aApproved = xBox->run() == RET_YES;
1806     }
1807 
1808     // if there are no used styles selected or the user approved the changes
1809     if ( bUsedStyle && !aApproved )
1810         return;
1811 
1812     for (auto const& elem : aList)
1813     {
1814         const OUString aTemplName(pTreeView->get_text(*elem));
1815         bDontUpdate = true; // To prevent the Treelistbox to shut down while deleting
1816         Execute_Impl( SID_STYLE_DELETE, aTemplName,
1817                       OUString(), static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()) );
1818 
1819         if (mxTreeBox->get_visible())
1820         {
1821             weld::RemoveParentKeepChildren(*mxTreeBox, *elem);
1822             bDontUpdate = false;
1823         }
1824     }
1825     bDontUpdate = false; //if everything is deleted set bDontUpdate back to false
1826     UpdateStyles_Impl(StyleFlags::UpdateFamilyList); //and force-update the list
1827 }
1828 
HideHdl()1829 void SfxCommonTemplateDialog_Impl::HideHdl()
1830 {
1831     if ( !IsInitialized() || !HasSelectedStyle() )
1832         return;
1833 
1834     weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get();
1835     pTreeView->selected_foreach([this, pTreeView](weld::TreeIter& rEntry){
1836         OUString aTemplName = pTreeView->get_text(rEntry);
1837 
1838         Execute_Impl( SID_STYLE_HIDE, aTemplName,
1839                       OUString(), static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()) );
1840 
1841         return false;
1842     });
1843 }
1844 
ShowHdl()1845 void SfxCommonTemplateDialog_Impl::ShowHdl()
1846 {
1847     if ( !IsInitialized() || !HasSelectedStyle() )
1848         return;
1849 
1850     weld::TreeView* pTreeView = mxTreeBox->get_visible() ? mxTreeBox.get() : mxFmtLb.get();
1851     pTreeView->selected_foreach([this, pTreeView](weld::TreeIter& rEntry){
1852         OUString aTemplName = pTreeView->get_text(rEntry);
1853 
1854         Execute_Impl( SID_STYLE_SHOW, aTemplName,
1855                       OUString(), static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()) );
1856 
1857         return false;
1858     });
1859 }
1860 
EnableDelete()1861 void SfxCommonTemplateDialog_Impl::EnableDelete()
1862 {
1863     bool bEnableDelete(false);
1864     if(IsInitialized() && HasSelectedStyle())
1865     {
1866         OSL_ENSURE(pStyleSheetPool, "No StyleSheetPool");
1867         const OUString aTemplName(GetSelectedEntry());
1868         const SfxStyleFamilyItem *pItem = GetFamilyItem_Impl();
1869         const SfxStyleFamily eFam = pItem->GetFamily();
1870         SfxStyleSearchBits nFilter = SfxStyleSearchBits::Auto;
1871         if (pItem->GetFilterList().size() > nActFilter)
1872             nFilter = pItem->GetFilterList()[nActFilter].nFlags;
1873         if(nFilter == SfxStyleSearchBits::Auto)    // automatic
1874             nFilter = nAppFilter;
1875         const SfxStyleSheetBase *pStyle =
1876             pStyleSheetPool->Find(aTemplName,eFam, mxTreeBox->get_visible() ? SfxStyleSearchBits::All : nFilter);
1877 
1878         OSL_ENSURE(pStyle, "Style not found");
1879         if (pStyle && pStyle->IsUserDefined())
1880         {
1881             if (pStyle->HasClearParentSupport() || !pStyle->IsUsed())
1882             {
1883                 bEnableDelete = true;
1884             }
1885             else if (pStyle->GetFamily() == SfxStyleFamily::Page)
1886             {
1887                 // Hack to allow Calc page styles to be deleted,
1888                 // remove when IsUsed is fixed for Calc page styles.
1889                 SfxViewFrame* pFrame = GetObjectShell()->GetFrame();
1890                 if (pFrame)
1891                 {
1892                     uno::Reference<frame::XFrame > xFrame = pFrame->GetFrame().GetFrameInterface();
1893                     if (vcl::CommandInfoProvider::GetModuleIdentifier(xFrame) == "com.sun.star.sheet.SpreadsheetDocument")
1894                     {
1895                         bEnableDelete = true;
1896                     }
1897                 }
1898             }
1899         }
1900     }
1901     EnableDel(bEnableDelete);
1902 }
1903 
IMPL_LINK(SfxCommonTemplateDialog_Impl,MousePressHdl,const MouseEvent &,rMEvt,bool)1904 IMPL_LINK(SfxCommonTemplateDialog_Impl, MousePressHdl, const MouseEvent&, rMEvt, bool)
1905 {
1906     m_nModifier = rMEvt.GetModifier();
1907     return false;
1908 }
1909 
1910 // Double-click on a style sheet in the ListBox is applied.
IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl,TreeListApplyHdl,weld::TreeView &,bool)1911 IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, TreeListApplyHdl, weld::TreeView&, bool)
1912 {
1913     // only if that region is allowed
1914     if ( IsInitialized() && nullptr != pFamilyState[nActFamily-1] &&
1915          !GetSelectedEntry().isEmpty() )
1916     {
1917         Execute_Impl(SID_STYLE_APPLY,
1918                      GetSelectedEntry(), OUString(),
1919                      static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()),
1920                      SfxStyleSearchBits::Auto, nullptr, &m_nModifier);
1921     }
1922     // After selecting a focused item if possible again on the app window
1923     if ( dynamic_cast< const SfxTemplateDialog_Impl* >(this) !=  nullptr )
1924     {
1925         SfxViewFrame *pViewFrame = pBindings->GetDispatcher_Impl()->GetFrame();
1926         SfxViewShell *pVu = pViewFrame->GetViewShell();
1927         vcl::Window *pAppWin = pVu ? pVu->GetWindow(): nullptr;
1928         if(pAppWin)
1929             pAppWin->GrabFocus();
1930     }
1931 
1932     return true;
1933 }
1934 
IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl,PreviewHdl,weld::Toggleable &,void)1935 IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, PreviewHdl, weld::Toggleable&, void)
1936 {
1937     std::shared_ptr<comphelper::ConfigurationChanges> batch( comphelper::ConfigurationChanges::create() );
1938     bool bCustomPreview = mxPreviewCheckbox->get_active();
1939     officecfg::Office::Common::StylesAndFormatting::Preview::set(bCustomPreview, batch );
1940     batch->commit();
1941 
1942     mxFmtLb->clear();
1943     mxFmtLb->set_column_custom_renderer(0, bCustomPreview);
1944     mxTreeBox->clear();
1945     mxTreeBox->set_column_custom_renderer(0, bCustomPreview);
1946 
1947     FamilySelect(nActFamily, true);
1948 }
1949 
1950 // Selection of a template during the Watercan-Status
IMPL_LINK(SfxCommonTemplateDialog_Impl,FmtSelectHdl,weld::TreeView &,rListBox,void)1951 IMPL_LINK(SfxCommonTemplateDialog_Impl, FmtSelectHdl, weld::TreeView&, rListBox, void)
1952 {
1953     std::unique_ptr<weld::TreeIter> xHdlEntry = rListBox.make_iterator();
1954     if (!rListBox.get_cursor(xHdlEntry.get()))
1955         return;
1956 
1957     SelectStyle(rListBox.get_text(*xHdlEntry), true);
1958 }
1959 
UpdateStyleDependents()1960 void SfxCommonTemplateDialog_Impl::UpdateStyleDependents()
1961 {
1962     // Trigger Help PI. Only when the watercan is on
1963     if ( IsInitialized() &&
1964          IsCheckedItem("watercan") &&
1965          // only if that region is allowed
1966          nullptr != pFamilyState[nActFamily-1] && IsSafeForWaterCan() )
1967     {
1968         Execute_Impl(SID_STYLE_WATERCAN,
1969                      "", "", 0);
1970         Execute_Impl(SID_STYLE_WATERCAN,
1971                      GetSelectedEntry(), "",
1972                      static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()));
1973     }
1974     EnableItem("watercan", !bWaterDisabled);
1975     EnableDelete();
1976 }
1977 
MenuSelect(const OString & rIdent)1978 void SfxCommonTemplateDialog_Impl::MenuSelect(const OString& rIdent)
1979 {
1980     sLastItemIdent = rIdent;
1981     if (sLastItemIdent.isEmpty())
1982         return;
1983     Application::PostUserEvent(
1984         LINK(this, SfxCommonTemplateDialog_Impl, MenuSelectAsyncHdl));
1985 }
1986 
IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl,MenuSelectAsyncHdl,void *,void)1987 IMPL_LINK_NOARG(SfxCommonTemplateDialog_Impl, MenuSelectAsyncHdl, void*, void)
1988 {
1989     if (sLastItemIdent == "new")
1990         NewHdl();
1991     else if (sLastItemIdent == "edit")
1992         EditHdl();
1993     else if (sLastItemIdent == "delete")
1994         DeleteHdl();
1995     else if (sLastItemIdent == "hide")
1996         HideHdl();
1997     else if (sLastItemIdent == "show")
1998         ShowHdl();
1999 }
2000 
GetActualFamily() const2001 SfxStyleFamily SfxCommonTemplateDialog_Impl::GetActualFamily() const
2002 {
2003     const SfxStyleFamilyItem *pFamilyItem = GetFamilyItem_Impl();
2004     if( !pFamilyItem || nActFamily == 0xffff )
2005         return SfxStyleFamily::Para;
2006     else
2007         return pFamilyItem->GetFamily();
2008 }
2009 
EnableExample_Impl(sal_uInt16 nId,bool bEnable)2010 void SfxCommonTemplateDialog_Impl::EnableExample_Impl(sal_uInt16 nId, bool bEnable)
2011 {
2012     bool bDisable = !bEnable || !IsSafeForWaterCan();
2013     if (nId == SID_STYLE_NEW_BY_EXAMPLE)
2014     {
2015         bNewByExampleDisabled = bDisable;
2016         EnableItem("new", bEnable);
2017         EnableItem("newmenu", bEnable);
2018     }
2019     else if( nId == SID_STYLE_UPDATE_BY_EXAMPLE )
2020     {
2021         bUpdateByExampleDisabled = bDisable;
2022         EnableItem("update", bEnable);
2023     }
2024 }
2025 
CreateContextMenu()2026 void SfxCommonTemplateDialog_Impl::CreateContextMenu()
2027 {
2028     if ( bBindingUpdate )
2029     {
2030         pBindings->Invalidate( SID_STYLE_NEW, true );
2031         pBindings->Update( SID_STYLE_NEW );
2032         bBindingUpdate = false;
2033     }
2034     mxMenu.reset();
2035     mxMenuBuilder.reset(Application::CreateBuilder(nullptr, "sfx/ui/stylecontextmenu.ui"));
2036     mxMenu = mxMenuBuilder->weld_menu("menu");
2037     mxMenu->set_sensitive("edit", bCanEdit);
2038     mxMenu->set_sensitive("delete", bCanDel);
2039     mxMenu->set_sensitive("new", bCanNew);
2040     mxMenu->set_sensitive("hide", bCanHide);
2041     mxMenu->set_sensitive("show", bCanShow);
2042 
2043     const SfxStyleFamilyItem* pItem = GetFamilyItem_Impl();
2044     if (pItem && pItem->GetFamily() == SfxStyleFamily::Table) //tdf#101648, no ui for this yet
2045     {
2046         mxMenu->set_sensitive("edit", false);
2047         mxMenu->set_sensitive("new", false);
2048     }
2049     if (pItem && pItem->GetFamily() == SfxStyleFamily::Pseudo)
2050     {
2051         const OUString aTemplName(GetSelectedEntry());
2052         if (aTemplName == "No List")
2053         {
2054             mxMenu->set_sensitive("edit", false);
2055             mxMenu->set_sensitive("new", false);
2056             mxMenu->set_sensitive("hide", false);
2057         }
2058     }
2059 }
2060 
SfxTemplateDialog_Impl(SfxBindings * pB,SfxTemplatePanelControl * pDlgWindow)2061 SfxTemplateDialog_Impl::SfxTemplateDialog_Impl(SfxBindings* pB, SfxTemplatePanelControl* pDlgWindow)
2062     : SfxCommonTemplateDialog_Impl(pB, pDlgWindow->get_container(), pDlgWindow->get_builder())
2063     , m_xActionTbL(pDlgWindow->get_builder()->weld_toolbar("left"))
2064     , m_xActionTbR(pDlgWindow->get_builder()->weld_toolbar("right"))
2065     , m_xToolMenu(pDlgWindow->get_builder()->weld_menu("toolmenu"))
2066     , m_nActionTbLVisible(0)
2067 {
2068     m_xActionTbR->set_item_help_id("watercan", HID_TEMPLDLG_WATERCAN);
2069     // shown/hidden in SfxTemplateDialog_Impl::ReplaceUpdateButtonByMenu()
2070     m_xActionTbR->set_item_help_id("new", HID_TEMPLDLG_NEWBYEXAMPLE);
2071     m_xActionTbR->set_item_help_id("newmenu", HID_TEMPLDLG_NEWBYEXAMPLE);
2072     m_xActionTbR->set_item_menu("newmenu", m_xToolMenu.get());
2073     m_xToolMenu->connect_activate(LINK(this, SfxTemplateDialog_Impl, ToolMenuSelectHdl));
2074     m_xActionTbR->set_item_help_id("update", HID_TEMPLDLG_UPDATEBYEXAMPLE);
2075 
2076     Initialize();
2077 }
2078 
2079 class ToolbarDropTarget final : public DropTargetHelper
2080 {
2081 private:
2082     SfxTemplateDialog_Impl& m_rParent;
2083 
2084 public:
ToolbarDropTarget(SfxTemplateDialog_Impl & rDialog,weld::Toolbar & rToolbar)2085     ToolbarDropTarget(SfxTemplateDialog_Impl& rDialog, weld::Toolbar& rToolbar)
2086         : DropTargetHelper(rToolbar.get_drop_target())
2087         , m_rParent(rDialog)
2088     {
2089     }
2090 
AcceptDrop(const AcceptDropEvent & rEvt)2091     virtual sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt) override
2092     {
2093         return m_rParent.AcceptToolbarDrop(rEvt, *this);
2094     }
2095 
ExecuteDrop(const ExecuteDropEvent & rEvt)2096     virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt) override
2097     {
2098         return m_rParent.ExecuteDrop(rEvt);
2099     }
2100 };
2101 
Initialize()2102 void SfxTemplateDialog_Impl::Initialize()
2103 {
2104     SfxCommonTemplateDialog_Impl::Initialize();
2105 
2106     m_xActionTbL->connect_clicked(LINK(this, SfxTemplateDialog_Impl, ToolBoxLSelect));
2107     m_xActionTbR->connect_clicked(LINK(this, SfxTemplateDialog_Impl, ToolBoxRSelect));
2108     m_xActionTbL->set_help_id(HID_TEMPLDLG_TOOLBOX_LEFT);
2109 
2110     m_xToolbarDropTargetHelper.reset(new ToolbarDropTarget(*this, *m_xActionTbL));
2111 }
2112 
EnableFamilyItem(sal_uInt16 nId,bool bEnable)2113 void SfxTemplateDialog_Impl::EnableFamilyItem(sal_uInt16 nId, bool bEnable)
2114 {
2115     m_xActionTbL->set_item_sensitive(OString::number(nId), bEnable);
2116 }
2117 
2118 // Insert element into dropdown filter "Frame Styles", "List Styles", etc.
InsertFamilyItem(sal_uInt16 nId,const SfxStyleFamilyItem & rItem)2119 void SfxTemplateDialog_Impl::InsertFamilyItem(sal_uInt16 nId, const SfxStyleFamilyItem &rItem)
2120 {
2121     OString sHelpId;
2122     switch( rItem.GetFamily() )
2123     {
2124         case SfxStyleFamily::Char:     sHelpId = ".uno:CharStyle"; break;
2125         case SfxStyleFamily::Para:     sHelpId = ".uno:ParaStyle"; break;
2126         case SfxStyleFamily::Frame:    sHelpId = ".uno:FrameStyle"; break;
2127         case SfxStyleFamily::Page:     sHelpId = ".uno:PageStyle"; break;
2128         case SfxStyleFamily::Pseudo:   sHelpId = ".uno:ListStyle"; break;
2129         case SfxStyleFamily::Table:    sHelpId = ".uno:TableStyle"; break;
2130         default: OSL_FAIL("unknown StyleFamily"); break;
2131     }
2132 
2133     OString sId(OString::number(nId));
2134     m_xActionTbL->set_item_visible(sId, true);
2135     m_xActionTbL->set_item_icon_name(sId, rItem.GetImage());
2136     m_xActionTbL->set_item_tooltip_text(sId, rItem.GetText());
2137     m_xActionTbL->set_item_help_id(sId, sHelpId);
2138     ++m_nActionTbLVisible;
2139 }
2140 
ReplaceUpdateButtonByMenu()2141 void SfxTemplateDialog_Impl::ReplaceUpdateButtonByMenu()
2142 {
2143     m_xActionTbR->set_item_visible("update", false);
2144     m_xActionTbR->set_item_visible("new", false);
2145     m_xActionTbR->set_item_visible("newmenu", true);
2146     FillToolMenu();
2147 }
2148 
ClearFamilyList()2149 void SfxTemplateDialog_Impl::ClearFamilyList()
2150 {
2151     for (int i = 0, nCount = m_xActionTbL->get_n_items(); i < nCount; ++i)
2152         m_xActionTbL->set_item_visible(m_xActionTbL->get_item_ident(i), false);
2153 
2154 }
2155 
InvalidateBindings()2156 void SfxCommonTemplateDialog_Impl::InvalidateBindings()
2157 {
2158     pBindings->Invalidate(SID_STYLE_NEW_BY_EXAMPLE, true);
2159     pBindings->Update( SID_STYLE_NEW_BY_EXAMPLE );
2160     pBindings->Invalidate(SID_STYLE_UPDATE_BY_EXAMPLE, true);
2161     pBindings->Update( SID_STYLE_UPDATE_BY_EXAMPLE );
2162     pBindings->Invalidate( SID_STYLE_WATERCAN, true);
2163     pBindings->Update( SID_STYLE_WATERCAN );
2164     pBindings->Invalidate( SID_STYLE_NEW, true);
2165     pBindings->Update( SID_STYLE_NEW );
2166     pBindings->Invalidate( SID_STYLE_DRAGHIERARCHIE, true);
2167     pBindings->Update( SID_STYLE_DRAGHIERARCHIE );
2168 }
2169 
~SfxTemplateDialog_Impl()2170 SfxTemplateDialog_Impl::~SfxTemplateDialog_Impl()
2171 {
2172     m_xToolbarDropTargetHelper.reset();
2173     m_xActionTbL.reset();
2174     m_xActionTbR.reset();
2175 }
2176 
EnableItem(const OString & rMesId,bool bCheck)2177 void SfxTemplateDialog_Impl::EnableItem(const OString& rMesId, bool bCheck)
2178 {
2179     if (rMesId == "watercan" && !bCheck && IsCheckedItem("watercan"))
2180         Execute_Impl(SID_STYLE_WATERCAN, "", "", 0);
2181     m_xActionTbR->set_item_sensitive(rMesId, bCheck);
2182 }
2183 
CheckItem(const OString & rMesId,bool bCheck)2184 void SfxTemplateDialog_Impl::CheckItem(const OString &rMesId, bool bCheck)
2185 {
2186     if (rMesId == "watercan")
2187     {
2188         bIsWater=bCheck;
2189         m_xActionTbR->set_item_active("watercan", bCheck);
2190     }
2191     else
2192         m_xActionTbL->set_item_active(rMesId, bCheck);
2193 }
2194 
IsCheckedItem(const OString & rMesId)2195 bool SfxTemplateDialog_Impl::IsCheckedItem(const OString& rMesId)
2196 {
2197     if (rMesId == "watercan")
2198         return m_xActionTbR->get_item_active("watercan");
2199     return m_xActionTbL->get_item_active(rMesId);
2200 }
2201 
IMPL_LINK(SfxTemplateDialog_Impl,ToolBoxLSelect,const OString &,rEntry,void)2202 IMPL_LINK( SfxTemplateDialog_Impl, ToolBoxLSelect, const OString&, rEntry, void)
2203 {
2204     FamilySelect(rEntry.toUInt32());
2205 }
2206 
IMPL_LINK(SfxTemplateDialog_Impl,ToolBoxRSelect,const OString &,rEntry,void)2207 IMPL_LINK(SfxTemplateDialog_Impl, ToolBoxRSelect, const OString&, rEntry, void)
2208 {
2209     if (rEntry == "newmenu")
2210         m_xActionTbR->set_menu_item_active(rEntry, !m_xActionTbR->get_menu_item_active(rEntry));
2211     else
2212         ActionSelect(rEntry);
2213 }
2214 
FillToolMenu()2215 void SfxTemplateDialog_Impl::FillToolMenu()
2216 {
2217     //create a popup menu in Writer
2218     OUString sTextDoc("com.sun.star.text.TextDocument");
2219 
2220     auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:StyleNewByExample", sTextDoc);
2221     OUString sLabel = vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties);
2222     m_xToolMenu->append("new", sLabel);
2223     aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:StyleUpdateByExample", sTextDoc);
2224     sLabel = vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties);
2225     m_xToolMenu->append("update", sLabel);
2226     m_xToolMenu->append_separator("separator");
2227 
2228     aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:LoadStyles", sTextDoc);
2229     sLabel = vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties);
2230     m_xToolMenu->append("load", sLabel);
2231 }
2232 
IMPL_LINK(SfxTemplateDialog_Impl,ToolMenuSelectHdl,const OString &,rMenuId,void)2233 IMPL_LINK(SfxTemplateDialog_Impl, ToolMenuSelectHdl, const OString&, rMenuId, void)
2234 {
2235     if (rMenuId.isEmpty())
2236         return;
2237     ActionSelect(rMenuId);
2238 }
2239 
SetFamily(SfxStyleFamily const nFamily)2240 void SfxCommonTemplateDialog_Impl::SetFamily(SfxStyleFamily const nFamily)
2241 {
2242     sal_uInt16 const nId(SfxTemplate::SfxFamilyIdToNId(nFamily));
2243     assert((0 < nId && nId <= MAX_FAMILIES) || 0xffff == nId);
2244     if ( nId != nActFamily )
2245     {
2246         if ( nActFamily != 0xFFFF )
2247             CheckItem(OString::number(nActFamily), false);
2248         nActFamily = nId;
2249         if ( nId != 0xFFFF )
2250             bUpdateFamily = true;
2251     }
2252 }
2253 
UpdateFamily_Impl()2254 void SfxCommonTemplateDialog_Impl::UpdateFamily_Impl()
2255 {
2256     bUpdateFamily = false;
2257 
2258     SfxDispatcher* pDispat = pBindings->GetDispatcher_Impl();
2259     SfxViewFrame *pViewFrame = pDispat->GetFrame();
2260     SfxObjectShell *pDocShell = pViewFrame->GetObjectShell();
2261 
2262     SfxStyleSheetBasePool *pOldStyleSheetPool = pStyleSheetPool;
2263     pStyleSheetPool = pDocShell? pDocShell->GetStyleSheetPool(): nullptr;
2264     if ( pOldStyleSheetPool != pStyleSheetPool )
2265     {
2266         if ( pOldStyleSheetPool )
2267             EndListening(*pOldStyleSheetPool);
2268         if ( pStyleSheetPool )
2269             StartListening(*pStyleSheetPool);
2270     }
2271 
2272     bWaterDisabled = false;
2273     bCanNew = mxTreeBox->get_visible() || mxFmtLb->count_selected_rows() <= 1;
2274     bTreeDrag = true;
2275     bUpdateByExampleDisabled = false;
2276 
2277     if (pStyleSheetPool)
2278     {
2279         if (!mxTreeBox->get_visible())
2280             UpdateStyles_Impl(StyleFlags::UpdateFamily | StyleFlags::UpdateFamilyList);
2281         else
2282         {
2283             UpdateStyles_Impl(StyleFlags::UpdateFamily);
2284             FillTreeBox();
2285         }
2286     }
2287 
2288     InvalidateBindings();
2289 
2290     if (IsCheckedItem("watercan") &&
2291          // only if that area is allowed
2292          nullptr != pFamilyState[nActFamily - 1])
2293     {
2294         Execute_Impl(SID_STYLE_APPLY,
2295                      GetSelectedEntry(),
2296                      OUString(),
2297                      static_cast<sal_uInt16>(GetFamilyItem_Impl()->GetFamily()));
2298     }
2299 }
2300 
ReplaceUpdateButtonByMenu()2301 void SfxCommonTemplateDialog_Impl::ReplaceUpdateButtonByMenu()
2302 {
2303     //does nothing
2304 }
2305 
AcceptToolbarDrop(const AcceptDropEvent & rEvt,const DropTargetHelper & rHelper)2306 sal_Int8 SfxTemplateDialog_Impl::AcceptToolbarDrop(const AcceptDropEvent& rEvt, const DropTargetHelper& rHelper)
2307 {
2308     sal_Int8 nReturn = DND_ACTION_NONE;
2309 
2310     // auto flip to the category under the mouse
2311     int nIndex = m_xActionTbL->get_drop_index(rEvt.maPosPixel);
2312     if (nIndex >= m_nActionTbLVisible)
2313         nIndex = m_nActionTbLVisible - 1;
2314 
2315     OString sIdent = m_xActionTbL->get_item_ident(nIndex);
2316     if (!sIdent.isEmpty() && !m_xActionTbL->get_item_active(sIdent))
2317         ToolBoxLSelect(sIdent);
2318 
2319     // special case: page styles are allowed to create new styles by example
2320     // but not allowed to be created by drag and drop
2321     if (sIdent.toUInt32() != SfxTemplate::SfxFamilyIdToNId(SfxStyleFamily::Page) &&
2322         rHelper.IsDropFormatSupported(SotClipboardFormatId::OBJECTDESCRIPTOR) &&
2323         !bNewByExampleDisabled)
2324     {
2325         nReturn = DND_ACTION_COPY;
2326     }
2327     return nReturn;
2328 }
2329 
2330 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2331