1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <sfx2/templatelocalview.hxx>
11 
12 #include <comphelper/string.hxx>
13 #include <sfx2/doctempl.hxx>
14 #include <sfx2/inputdlg.hxx>
15 #include <sfx2/sfxresid.hxx>
16 #include <templatecontaineritem.hxx>
17 #include <templateviewitem.hxx>
18 #include <sfx2/docfac.hxx>
19 #include <tools/urlobj.hxx>
20 #include <unotools/moduleoptions.hxx>
21 #include <vcl/svapp.hxx>
22 #include <vcl/weld.hxx>
23 #include <vcl/commandevent.hxx>
24 #include <vcl/event.hxx>
25 
26 #include <sfx2/strings.hrc>
27 #include <bitmaps.hlst>
28 
29 using namespace ::com::sun::star;
30 
31 bool ViewFilter_Application::isFilteredExtension(FILTER_APPLICATION filter, std::u16string_view rExt)
32 {
33     bool bRet = rExt == u"ott" || rExt == u"stw" || rExt == u"oth" || rExt == u"dot" || rExt == u"dotx" || rExt == u"otm"
34           || rExt == u"ots" || rExt == u"stc" || rExt == u"xlt" || rExt == u"xltm" || rExt == u"xltx"
35           || rExt == u"otp" || rExt == u"sti" || rExt == u"pot" || rExt == u"potm" || rExt == u"potx"
36           || rExt == u"otg" || rExt == u"std";
37 
38     if (filter == FILTER_APPLICATION::WRITER)
39     {
40         bRet = rExt == u"ott" || rExt == u"stw" || rExt == u"oth" || rExt == u"dot" || rExt == u"dotx" || rExt == u"otm";
41     }
42     else if (filter == FILTER_APPLICATION::CALC)
43     {
44         bRet = rExt == u"ots" || rExt == u"stc" || rExt == u"xlt" || rExt == u"xltm" || rExt == u"xltx";
45     }
46     else if (filter == FILTER_APPLICATION::IMPRESS)
47     {
48         bRet = rExt == u"otp" || rExt == u"sti" || rExt == u"pot" || rExt == u"potm" || rExt == u"potx";
49     }
50     else if (filter == FILTER_APPLICATION::DRAW)
51     {
52         bRet = rExt == u"otg" || rExt == u"std";
53     }
54 
55     return bRet;
56 }
57 
58 bool ViewFilter_Application::isValid (const OUString &rPath) const
59 {
60     INetURLObject aUrl(rPath);
61     return isFilteredExtension(mApp, aUrl.getExtension());
62 }
63 
64 bool ViewFilter_Application::operator () (const ThumbnailViewItem *pItem)
65 {
66     const TemplateViewItem *pTempItem = dynamic_cast<const TemplateViewItem*>(pItem);
67     if (pTempItem)
68         return isValid(pTempItem->getPath());
69 
70     return true;
71 }
72 
73 void TemplateLocalView::updateThumbnailDimensions(tools::Long itemMaxSize)
74 {
75     mnThumbnailWidth = itemMaxSize;
76     mnThumbnailHeight = itemMaxSize;
77 }
78 
79 TemplateLocalView::TemplateLocalView(std::unique_ptr<weld::ScrolledWindow> xWindow,
80                                            std::unique_ptr<weld::Menu> xMenu)
81     : ThumbnailView(std::move(xWindow), std::move(xMenu))
82     , mnCurRegionId(0)
83     , maSelectedItem(nullptr)
84     , mnThumbnailWidth(TEMPLATE_THUMBNAIL_MAX_WIDTH)
85     , mnThumbnailHeight(TEMPLATE_THUMBNAIL_MAX_HEIGHT)
86     , maPosition(0,0)
87     , mpDocTemplates(new SfxDocumentTemplates)
88 {
89 }
90 
91 TemplateLocalView::~TemplateLocalView()
92 {
93 }
94 
95 void TemplateLocalView::Populate()
96 {
97     maRegions.clear();
98     maAllTemplates.clear();
99 
100     sal_uInt16 nCount = mpDocTemplates->GetRegionCount();
101     for (sal_uInt16 i = 0; i < nCount; ++i)
102     {
103         OUString aRegionName(mpDocTemplates->GetFullRegionName(i));
104 
105         std::unique_ptr<TemplateContainerItem> pItem(new TemplateContainerItem( i+1 ));
106         pItem->mnRegionId = i;
107         pItem->maTitle = aRegionName;
108 
109         sal_uInt16 nEntries = mpDocTemplates->GetCount(i);
110 
111         for (sal_uInt16 j = 0; j < nEntries; ++j)
112         {
113             OUString aName = mpDocTemplates->GetName(i,j);
114             OUString aURL = mpDocTemplates->GetPath(i,j);
115 
116             TemplateItemProperties aProperties;
117             aProperties.nId = j+1;
118             aProperties.nDocId = j;
119             aProperties.nRegionId = i;
120             aProperties.aName = aName;
121             aProperties.aPath = aURL;
122             aProperties.aRegionName = aRegionName;
123             aProperties.aThumbnail = TemplateLocalView::fetchThumbnail(aURL,
124                                                                           mnThumbnailWidth,
125                                                                           mnThumbnailHeight);
126 
127             pItem->maTemplates.push_back(aProperties);
128             maAllTemplates.push_back(aProperties);
129         }
130 
131         maRegions.push_back(std::move(pItem));
132     }
133 }
134 
135 void TemplateLocalView::reload()
136 {
137     mpDocTemplates->Update();
138 
139     Populate();
140 
141     // Check if we are currently browsing a region or root folder
142     if (mnCurRegionId)
143     {
144         sal_uInt16 nRegionId = mnCurRegionId - 1;   //Is offset by 1
145 
146         for (auto const & pRegion : maRegions)
147         {
148             if (pRegion->mnRegionId == nRegionId)
149             {
150                 showRegion(pRegion.get());
151                 break;
152             }
153         }
154     }
155     else
156         showAllTemplates();
157 
158     //No items should be selected by default
159     deselectItems();
160 }
161 
162 void TemplateLocalView::showAllTemplates()
163 {
164     mnCurRegionId = 0;
165 
166     insertItems(maAllTemplates, false, true);
167 
168     maOpenRegionHdl.Call(nullptr);
169 }
170 
171 void TemplateLocalView::showRegion(TemplateContainerItem const *pItem)
172 {
173     mnCurRegionId = pItem->mnRegionId+1;
174 
175     insertItems(pItem->maTemplates);
176 
177     maOpenRegionHdl.Call(nullptr);
178 }
179 
180 TemplateContainerItem* TemplateLocalView::getRegion(std::u16string_view rName)
181 {
182     for (auto const & pRegion : maRegions)
183         if (pRegion->maTitle == rName)
184             return pRegion.get();
185 
186     return nullptr;
187 }
188 
189 void TemplateLocalView::ContextMenuSelectHdl(std::string_view  rIdent)
190 {
191     if (rIdent == "open")
192         maOpenTemplateHdl.Call(maSelectedItem);
193     else if (rIdent == "edit")
194         maEditTemplateHdl.Call(maSelectedItem);
195     else if (rIdent == "rename")
196     {
197         InputDialog aTitleEditDlg(GetDrawingArea(), SfxResId(STR_RENAME_TEMPLATE));
198         OUString sOldTitle = maSelectedItem->getTitle();
199         aTitleEditDlg.SetEntryText(sOldTitle);
200         aTitleEditDlg.HideHelpBtn();
201 
202         if (!aTitleEditDlg.run())
203             return;
204         OUString sNewTitle = comphelper::string::strip(aTitleEditDlg.GetEntryText(), ' ');
205 
206         if ( !sNewTitle.isEmpty() && sNewTitle != sOldTitle )
207         {
208             maSelectedItem->setTitle(sNewTitle);
209         }
210     }
211     else if (rIdent == "delete")
212     {
213         std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo,
214                                                        SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE)));
215         if (xQueryDlg->run() != RET_YES)
216             return;
217 
218         maDeleteTemplateHdl.Call(maSelectedItem);
219         reload();
220     }
221     else if (rIdent == "default")
222         maDefaultTemplateHdl.Call(maSelectedItem);
223 }
224 
225 sal_uInt16 TemplateLocalView::getRegionId(size_t pos) const
226 {
227     assert(pos < maRegions.size());
228 
229     return maRegions[pos]->mnId;
230 }
231 
232 sal_uInt16 TemplateLocalView::getRegionId(std::u16string_view sRegion) const
233 {
234     for (auto const & pRegion : maRegions)
235     {
236         if (pRegion->maTitle == sRegion)
237             return pRegion->mnId;
238     }
239 
240     return 0;
241 }
242 
243 OUString TemplateLocalView::getRegionName(const sal_uInt16 nRegionId) const
244 {
245     return mpDocTemplates->GetRegionName(nRegionId);
246 }
247 
248 OUString TemplateLocalView::getRegionItemName(const sal_uInt16 nItemId) const
249 {
250     for (auto const & pRegion : maRegions)
251     {
252         if (pRegion->mnId == nItemId)
253             return pRegion->maTitle;
254     }
255 
256     return OUString();
257 }
258 
259 std::vector<OUString> TemplateLocalView::getFolderNames()
260 {
261     size_t n = maRegions.size();
262     std::vector<OUString> ret(n);
263 
264     for (size_t i = 0; i < n; ++i)
265         ret[i] = maRegions[i]->maTitle;
266 
267     return ret;
268 }
269 
270 std::vector<TemplateItemProperties>
271 TemplateLocalView::getFilteredItems(const std::function<bool (const TemplateItemProperties&)> &rFunc) const
272 {
273     std::vector<TemplateItemProperties> aItems;
274 
275     if (mnCurRegionId)
276     {
277         TemplateContainerItem *pFolderItem = maRegions[mnCurRegionId-1].get();
278 
279         for (const TemplateItemProperties & rItemProps : pFolderItem->maTemplates)
280         {
281             if (rFunc(rItemProps))
282                 aItems.push_back(rItemProps);
283         }
284     }
285     else
286     {
287         for (auto const & pFolderItem : maRegions)
288         {
289             for (const TemplateItemProperties & rItemProps : pFolderItem->maTemplates)
290             {
291                 if (rFunc(rItemProps))
292                     aItems.push_back(rItemProps);
293             }
294         }
295     }
296 
297     return aItems;
298 }
299 
300 sal_uInt16 TemplateLocalView::createRegion(const OUString &rName)
301 {
302     sal_uInt16 nRegionId = mpDocTemplates->GetRegionCount();    // Next regionId
303     sal_uInt16 nItemId = getNextItemId();
304 
305     if (!mpDocTemplates->InsertDir(rName,nRegionId))
306         return 0;
307 
308     // Insert to the region cache list and to the thumbnail item list
309     std::unique_ptr<TemplateContainerItem> pItem(new TemplateContainerItem( nItemId ));
310     pItem->mnRegionId = nRegionId;
311     pItem->maTitle = rName;
312 
313     maRegions.push_back(std::move(pItem));
314 
315     return nItemId;
316 }
317 
318 bool TemplateLocalView::renameRegion(std::u16string_view rTitle, const OUString &rNewTitle)
319 {
320     TemplateContainerItem *pRegion = getRegion(rTitle);
321 
322     if(pRegion)
323     {
324         sal_uInt16 nRegionId = pRegion->mnRegionId;
325         return mpDocTemplates->SetName( rNewTitle, nRegionId, USHRT_MAX/*nDocId*/ );
326     }
327     return false;
328 }
329 
330 bool TemplateLocalView::removeRegion(const sal_uInt16 nItemId)
331 {
332     sal_uInt16 nRegionId = USHRT_MAX;
333 
334     // Remove from the region cache list
335     for (auto pRegionIt = maRegions.begin(); pRegionIt != maRegions.end();)
336     {
337         if ( (*pRegionIt)->mnId == nItemId )
338         {
339             if (!mpDocTemplates->Delete((*pRegionIt)->mnRegionId,USHRT_MAX))
340                 return false;
341 
342             nRegionId = (*pRegionIt)->mnRegionId;
343 
344             pRegionIt = maRegions.erase(pRegionIt);
345         }
346         else
347         {
348             // Synchronize regions cache ids with SfxDocumentTemplates
349             if (nRegionId != USHRT_MAX && (*pRegionIt)->mnRegionId > nRegionId)
350                 --(*pRegionIt)->mnRegionId;
351 
352             ++pRegionIt;
353         }
354     }
355 
356     if (nRegionId == USHRT_MAX)
357         return false;
358 
359     // Synchronize view regions ids with SfxDocumentTemplates
360     for (auto const& region : maRegions)
361     {
362         if (region->mnRegionId > nRegionId)
363             --region->mnRegionId;
364     }
365 
366     return true;
367 }
368 
369 bool TemplateLocalView::removeTemplate (const sal_uInt16 nItemId, const sal_uInt16 nSrcItemId)
370 {
371     for (auto const & pRegion : maRegions)
372     {
373         if (pRegion->mnId == nSrcItemId)
374         {
375             TemplateContainerItem *pItem = pRegion.get();
376             auto pIter = std::find_if(pItem->maTemplates.begin(), pItem->maTemplates.end(),
377                 [nItemId](const TemplateItemProperties& rTemplate) { return rTemplate.nId == nItemId; });
378             if (pIter != pItem->maTemplates.end())
379             {
380                 if (!mpDocTemplates->Delete(pItem->mnRegionId,pIter->nDocId))
381                     return false;
382 
383                 pIter = pItem->maTemplates.erase(pIter);
384 
385                 if (pRegion->mnRegionId == mnCurRegionId-1)
386                 {
387                     RemoveItem(nItemId);
388                     Invalidate();
389                 }
390 
391                 // Update Doc Idx for all templates that follow
392                 for (; pIter != pItem->maTemplates.end(); ++pIter)
393                     pIter->nDocId = pIter->nDocId - 1;
394             }
395 
396             CalculateItemPositions();
397             break;
398         }
399     }
400 
401     return true;
402 }
403 
404 bool TemplateLocalView::moveTemplate (const ThumbnailViewItem *pItem, const sal_uInt16 nSrcItem,
405                                        const sal_uInt16 nTargetItem)
406 {
407     TemplateContainerItem *pTarget = nullptr;
408     TemplateContainerItem *pSrc = nullptr;
409 
410     for (auto const & pRegion : maRegions)
411     {
412         if (pRegion->mnId == nTargetItem)
413             pTarget = pRegion.get();
414         else if (pRegion->mnId == nSrcItem)
415             pSrc = pRegion.get();
416     }
417 
418     if (pTarget && pSrc)
419     {
420         sal_uInt16 nSrcRegionId = pSrc->mnRegionId;
421         sal_uInt16 nTargetRegion = pTarget->mnRegionId;
422         sal_uInt16 nTargetIdx = mpDocTemplates->GetCount(nTargetRegion);    // Next Idx
423 
424         const TemplateViewItem *pViewItem = static_cast<const TemplateViewItem*>(pItem);
425 
426         bool bCopy = !mpDocTemplates->Move(nTargetRegion,nTargetIdx,nSrcRegionId,pViewItem->mnDocId);
427 
428         if (bCopy)
429         {
430             OUString sQuery = SfxResId(STR_MSG_QUERY_COPY).replaceFirst("$1", pViewItem->maTitle).replaceFirst("$2",
431                 getRegionName(nTargetRegion));
432 
433             std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo, sQuery));
434             if (xQueryDlg->run() != RET_YES)
435                 return false;
436 
437             if (!mpDocTemplates->Copy(nTargetRegion,nTargetIdx,nSrcRegionId,pViewItem->mnDocId))
438                 return false;
439         }
440         // move template to destination
441 
442         TemplateItemProperties aTemplateItem;
443         aTemplateItem.nId = nTargetIdx + 1;
444         aTemplateItem.nDocId = nTargetIdx;
445         aTemplateItem.nRegionId = nTargetRegion;
446         aTemplateItem.aName = pViewItem->maTitle;
447         aTemplateItem.aPath = mpDocTemplates->GetPath(nTargetRegion,nTargetIdx);
448         aTemplateItem.aRegionName = pViewItem->maHelpText;
449         aTemplateItem.aThumbnail = pViewItem->maPreview1;
450 
451         pTarget->maTemplates.push_back(aTemplateItem);
452 
453         if (!bCopy)
454         {
455             // remove template from region cached data
456 
457             std::vector<TemplateItemProperties>::iterator aIter;
458             for (aIter = pSrc->maTemplates.begin(); aIter != pSrc->maTemplates.end();)
459             {
460                 if (aIter->nDocId == pViewItem->mnDocId)
461                 {
462                     aIter = pSrc->maTemplates.erase(aIter);
463                 }
464                 else
465                 {
466                     // Keep region document id synchronized with SfxDocumentTemplates
467                     if (aIter->nDocId > pViewItem->mnDocId)
468                         --aIter->nDocId;
469 
470                     ++aIter;
471                 }
472             }
473 
474             // Keep view document id synchronized with SfxDocumentTemplates
475             for (auto const& item : mItemList)
476             {
477                 auto pTemplateViewItem = static_cast<TemplateViewItem*>(item.get());
478                 if (pTemplateViewItem->mnDocId > pViewItem->mnDocId)
479                     --pTemplateViewItem->mnDocId;
480             }
481         }
482 
483         CalculateItemPositions();
484         Invalidate();
485 
486         return true;
487     }
488 
489     return false;
490 }
491 
492 void TemplateLocalView::moveTemplates(const std::set<const ThumbnailViewItem*, selection_cmp_fn> &rItems,
493                                       const sal_uInt16 nTargetItem)
494 {
495     TemplateContainerItem *pTarget = nullptr;
496     TemplateContainerItem *pSrc = nullptr;
497 
498     for (auto const & pRegion : maRegions)
499     {
500         if (pRegion->mnId == nTargetItem)
501             pTarget = pRegion.get();
502     }
503 
504     if (!pTarget)
505         return;
506 
507     bool refresh = false;
508 
509     sal_uInt16 nTargetRegion = pTarget->mnRegionId;
510     sal_uInt16 nTargetIdx = mpDocTemplates->GetCount(nTargetRegion);    // Next Idx
511     std::vector<sal_uInt16> aItemIds;    // List of moved items ids (also prevents the invalidation of rItems iterators when we remove them as we go)
512 
513     std::set<const ThumbnailViewItem*,selection_cmp_fn>::const_iterator aSelIter;
514     for ( aSelIter = rItems.begin(); aSelIter != rItems.end(); ++aSelIter, ++nTargetIdx )
515     {
516         const TemplateViewItem *pViewItem = static_cast<const TemplateViewItem*>(*aSelIter);
517         sal_uInt16 nSrcRegionId = pViewItem->mnRegionId;
518 
519         for (auto const & pRegion : maRegions)
520         {
521             if (pRegion->mnRegionId == nSrcRegionId)
522                 pSrc = pRegion.get();
523         }
524 
525         if(pSrc)
526         {
527             bool bCopy = !mpDocTemplates->Move(nTargetRegion,nTargetIdx,nSrcRegionId,pViewItem->mnDocId);
528 
529             if (bCopy)
530             {
531                 OUString sQuery = SfxResId(STR_MSG_QUERY_COPY).replaceFirst("$1", pViewItem->maTitle).replaceFirst("$2",
532                     getRegionName(nTargetRegion));
533                 std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo, sQuery));
534                 if (xQueryDlg->run() != RET_YES)
535                 {
536                     OUString sMsg(SfxResId(STR_MSG_ERROR_LOCAL_MOVE));
537                     sMsg = sMsg.replaceFirst("$1",getRegionName(nTargetRegion));
538                     std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetDrawingArea(),
539                                                               VclMessageType::Warning, VclButtonsType::Ok, sMsg.replaceFirst( "$2",pViewItem->maTitle)));
540                     xBox->run();
541 
542                     return; //return if any single move operation fails
543                 }
544 
545                 if (!mpDocTemplates->Copy(nTargetRegion,nTargetIdx,nSrcRegionId,pViewItem->mnDocId))
546                 {
547                     continue;
548                 }
549             }
550 
551             // move template to destination
552 
553             TemplateItemProperties aTemplateItem;
554             aTemplateItem.nId = nTargetIdx + 1;
555             aTemplateItem.nDocId = nTargetIdx;
556             aTemplateItem.nRegionId = nTargetRegion;
557             aTemplateItem.aName = pViewItem->maTitle;
558             aTemplateItem.aPath = mpDocTemplates->GetPath(nTargetRegion,nTargetIdx);
559             aTemplateItem.aRegionName = pViewItem->maHelpText;
560             aTemplateItem.aThumbnail = pViewItem->maPreview1;
561 
562             pTarget->maTemplates.push_back(aTemplateItem);
563 
564             if (!bCopy)
565             {
566                 // remove template from region cached data
567 
568                 std::vector<TemplateItemProperties>::iterator pPropIter;
569                 for (pPropIter = pSrc->maTemplates.begin(); pPropIter != pSrc->maTemplates.end();)
570                 {
571                     if (pPropIter->nDocId == pViewItem->mnDocId)
572                     {
573                         pPropIter = pSrc->maTemplates.erase(pPropIter);
574                         aItemIds.push_back(pViewItem->mnDocId + 1);//mnid
575                     }
576                     else
577                     {
578                         // Keep region document id synchronized with SfxDocumentTemplates
579                         if (pPropIter->nDocId > pViewItem->mnDocId)
580                             --pPropIter->nDocId;
581 
582                         ++pPropIter;
583                     }
584                 }
585 
586                 // Keep view document id synchronized with SfxDocumentTemplates
587                 for (auto const& item : mItemList)
588                 {
589                     auto pTemplateViewItem = static_cast<TemplateViewItem*>(item.get());
590                     if (pTemplateViewItem->mnDocId > pViewItem->mnDocId)
591                         --pTemplateViewItem->mnDocId;
592                 }
593             }
594         }
595 
596         refresh = true;
597     }
598 
599     // Remove items from the current view
600     for (auto const& itemId : aItemIds)
601         RemoveItem(itemId);
602 
603     if (refresh)
604     {
605         CalculateItemPositions();
606         Invalidate();
607     }
608 }
609 
610 bool TemplateLocalView::copyFrom (TemplateContainerItem *pItem, const OUString &rPath)
611 {
612     sal_uInt16 nId = 1;
613     sal_uInt16 nDocId = 0;
614     sal_uInt16 nRegionId = pItem->mnRegionId;
615     OUString aPath(rPath);
616 
617     if (!pItem->maTemplates.empty())
618     {
619         nId = pItem->maTemplates.back().nId+1;
620         nDocId = pItem->maTemplates.back().nDocId+1;
621     }
622 
623     if (mpDocTemplates->CopyFrom(nRegionId,nDocId,aPath))
624     {
625         TemplateItemProperties aTemplate;
626         aTemplate.nId = nId;
627         aTemplate.nDocId = nDocId;
628         aTemplate.nRegionId = nRegionId;
629         aTemplate.aName = aPath;
630         aTemplate.aThumbnail = TemplateLocalView::fetchThumbnail(rPath,
631                                                                     TEMPLATE_THUMBNAIL_MAX_WIDTH,
632                                                                     TEMPLATE_THUMBNAIL_MAX_HEIGHT);
633         aTemplate.aPath = rPath;
634         aTemplate.aRegionName = getRegionName(nRegionId);
635 
636         pItem->maTemplates.push_back(aTemplate);
637 
638         CalculateItemPositions();
639 
640         return true;
641     }
642 
643     return false;
644 }
645 
646 bool TemplateLocalView::exportTo(const sal_uInt16 nItemId, const sal_uInt16 nRegionItemId, const OUString &rName)
647 {
648     for (auto const & pRegItem : maRegions)
649     {
650         if (pRegItem->mnId == nRegionItemId)
651         {
652             for (auto const& elem : pRegItem->maTemplates)
653             {
654                 if (elem.nId == nItemId)
655                 {
656                     return mpDocTemplates->CopyTo(pRegItem->mnRegionId,elem.nDocId,rName);
657                 }
658             }
659 
660             break;
661         }
662     }
663 
664     return false;
665 }
666 
667 bool TemplateLocalView::renameItem(ThumbnailViewItem* pItem, const OUString& sNewTitle)
668 {
669     sal_uInt16 nRegionId = 0;
670     sal_uInt16 nDocId = USHRT_MAX;
671     TemplateViewItem* pDocItem = dynamic_cast<TemplateViewItem*>( pItem );
672 
673     if ( pDocItem )
674     {
675         nRegionId = pDocItem->mnRegionId;
676         nDocId = pDocItem->mnDocId;
677     }
678 
679     return mpDocTemplates->SetName( sNewTitle, nRegionId, nDocId );
680 }
681 
682 void TemplateLocalView::insertItems(const std::vector<TemplateItemProperties> &rTemplates, bool isRegionSelected, bool bShowCategoryInTooltip)
683 {
684     std::vector<std::unique_ptr<ThumbnailViewItem>> aItems(rTemplates.size());
685     for (size_t i = 0, n = rTemplates.size(); i < n; ++i )
686     {
687         const TemplateItemProperties *pCur = &rTemplates[i];
688 
689         std::unique_ptr<TemplateViewItem> pChild;
690         if(isRegionSelected)
691             pChild.reset(new TemplateViewItem(*this, pCur->nId));
692         else
693             pChild.reset(new TemplateViewItem(*this, i+1));
694 
695         pChild->mnDocId = pCur->nDocId;
696         pChild->mnRegionId = pCur->nRegionId;
697         pChild->maTitle = pCur->aName;
698         pChild->setPath(pCur->aPath);
699 
700         if(!bShowCategoryInTooltip)
701             pChild->setHelpText(pCur->aName);
702         else
703         {
704             OUString sHelpText = SfxResId(STR_TEMPLATE_TOOLTIP);
705             sHelpText = (sHelpText.replaceFirst("$1", pCur->aName)).replaceFirst("$2", pCur->aRegionName);
706             pChild->setHelpText(sHelpText);
707         }
708 
709         pChild->maPreview1 = pCur->aThumbnail;
710 
711         if(IsDefaultTemplate(pCur->aPath))
712             pChild->showDefaultIcon(true);
713 
714         if ( pCur->aThumbnail.IsEmpty() )
715         {
716             // Use the default thumbnail if we have nothing else
717             pChild->maPreview1 = TemplateLocalView::getDefaultThumbnail(pCur->aPath);
718         }
719 
720         aItems[i] = std::move(pChild);
721     }
722 
723     updateItems(std::move(aItems));
724 }
725 
726 bool TemplateLocalView::MouseButtonDown( const MouseEvent& rMEvt )
727 {
728     GrabFocus();
729     return ThumbnailView::MouseButtonDown(rMEvt);
730 }
731 
732 bool TemplateLocalView::Command(const CommandEvent& rCEvt)
733 {
734     if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
735         return CustomWidgetController::Command(rCEvt);
736 
737     if (rCEvt.IsMouseEvent())
738     {
739         deselectItems();
740         size_t nPos = ImplGetItem(rCEvt.GetMousePosPixel());
741         Point aPosition(rCEvt.GetMousePosPixel());
742         maPosition = aPosition;
743         ThumbnailViewItem* pItem = ImplGetItem(nPos);
744         const TemplateViewItem *pViewItem = dynamic_cast<const TemplateViewItem*>(pItem);
745 
746         if(pViewItem)
747         {
748             maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem);
749             maCreateContextMenuHdl.Call(pItem);
750         }
751     }
752     else
753     {
754         for (ThumbnailViewItem* pItem : mFilteredItemList)
755         {
756             //create context menu for the first selected item
757             if (pItem->isSelected())
758             {
759                 deselectItems();
760                 pItem->setSelection(true);
761                 maItemStateHdl.Call(pItem);
762                 tools::Rectangle aRect = pItem->getDrawArea();
763                 maPosition = aRect.Center();
764                 maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem);
765                 maCreateContextMenuHdl.Call(pItem);
766                 break;
767             }
768         }
769     }
770     return true;
771 }
772 
773 bool TemplateLocalView::KeyInput( const KeyEvent& rKEvt )
774 {
775     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
776 
777     if(aKeyCode == ( KEY_MOD1 | KEY_A ) )
778     {
779         for (ThumbnailViewItem* pItem : mFilteredItemList)
780         {
781             if (!pItem->isSelected())
782             {
783                 pItem->setSelection(true);
784                 maItemStateHdl.Call(pItem);
785             }
786         }
787 
788         if (IsReallyVisible() && IsUpdateMode())
789             Invalidate();
790         return true;
791     }
792     else if( aKeyCode == KEY_DELETE && !mFilteredItemList.empty())
793     {
794         std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo,
795                                                        SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE)));
796         if (xQueryDlg->run() != RET_YES)
797             return true;
798 
799         //copy to avoid changing filtered item list during deletion
800         ThumbnailValueItemList mFilteredItemListCopy = mFilteredItemList;
801 
802         for (ThumbnailViewItem* pItem : mFilteredItemListCopy)
803         {
804             if (pItem->isSelected())
805             {
806                 maDeleteTemplateHdl.Call(pItem);
807             }
808         }
809         reload();
810     }
811 
812     return ThumbnailView::KeyInput(rKEvt);
813 }
814 
815 void TemplateLocalView::setOpenRegionHdl(const Link<void*,void> &rLink)
816 {
817     maOpenRegionHdl = rLink;
818 }
819 
820 void TemplateLocalView::setCreateContextMenuHdl(const Link<ThumbnailViewItem*,void> &rLink)
821 {
822     maCreateContextMenuHdl = rLink;
823 }
824 
825 void TemplateLocalView::setOpenTemplateHdl(const Link<ThumbnailViewItem*,void> &rLink)
826 {
827     maOpenTemplateHdl = rLink;
828 }
829 
830 void TemplateLocalView::setEditTemplateHdl(const Link<ThumbnailViewItem*,void> &rLink)
831 {
832     maEditTemplateHdl = rLink;
833 }
834 
835 void TemplateLocalView::setDeleteTemplateHdl(const Link<ThumbnailViewItem*,void> &rLink)
836 {
837     maDeleteTemplateHdl = rLink;
838 }
839 
840 void TemplateLocalView::setDefaultTemplateHdl(const Link<ThumbnailViewItem*,void> &rLink)
841 {
842     maDefaultTemplateHdl = rLink;
843 }
844 
845 BitmapEx TemplateLocalView::scaleImg (const BitmapEx &rImg, tools::Long width, tools::Long height)
846 {
847     BitmapEx aImg = rImg;
848 
849     if (!rImg.IsEmpty())
850     {
851         Size aSize = rImg.GetSizePixel();
852 
853         if (aSize.Width() == 0)
854             aSize.setWidth( 1 );
855 
856         if (aSize.Height() == 0)
857             aSize.setHeight( 1 );
858 
859         // make the picture fit the given width/height constraints
860         double nRatio = std::min(double(width)/double(aSize.Width()), double(height)/double(aSize.Height()));
861 
862         aImg.Scale(Size(aSize.Width() * nRatio, aSize.Height() * nRatio));
863     }
864 
865     return aImg;
866 }
867 
868 bool TemplateLocalView::IsDefaultTemplate(const OUString& rPath)
869 {
870     SvtModuleOptions aModOpt;
871     const css::uno::Sequence<OUString> &aServiceNames = aModOpt.GetAllServiceNames();
872 
873     return std::any_of(aServiceNames.begin(), aServiceNames.end(), [&rPath](const OUString& rName) {
874         return SfxObjectFactory::GetStandardTemplate(rName).match(rPath); });
875 }
876 
877 void TemplateLocalView::RemoveDefaultTemplateIcon(std::u16string_view rPath)
878 {
879     for (const std::unique_ptr<ThumbnailViewItem>& pItem : mItemList)
880     {
881         TemplateViewItem* pViewItem = dynamic_cast<TemplateViewItem*>(pItem.get());
882         if (pViewItem && pViewItem->getPath().match(rPath))
883         {
884             pViewItem->showDefaultIcon(false);
885             Invalidate();
886             return;
887         }
888     }
889 }
890 
891 BitmapEx TemplateLocalView::getDefaultThumbnail( const OUString& rPath )
892 {
893     BitmapEx aImg;
894     INetURLObject aUrl(rPath);
895     OUString aExt = aUrl.getExtension();
896 
897     if ( ViewFilter_Application::isFilteredExtension( FILTER_APPLICATION::WRITER, aExt) )
898         aImg = BitmapEx(SFX_THUMBNAIL_TEXT);
899     else if ( ViewFilter_Application::isFilteredExtension( FILTER_APPLICATION::CALC, aExt) )
900         aImg = BitmapEx(SFX_THUMBNAIL_SHEET);
901     else if ( ViewFilter_Application::isFilteredExtension( FILTER_APPLICATION::IMPRESS, aExt) )
902         aImg = BitmapEx(SFX_THUMBNAIL_PRESENTATION);
903     else if ( ViewFilter_Application::isFilteredExtension( FILTER_APPLICATION::DRAW, aExt) )
904         aImg = BitmapEx(SFX_THUMBNAIL_DRAWING);
905 
906     return aImg;
907 }
908 
909 BitmapEx TemplateLocalView::fetchThumbnail (const OUString &msURL, tools::Long width, tools::Long height)
910 {
911     return TemplateLocalView::scaleImg(ThumbnailView::readThumbnail(msURL), width, height);
912 }
913 
914 void TemplateLocalView::OnItemDblClicked (ThumbnailViewItem *pItem)
915 {
916     TemplateViewItem* pViewItem = dynamic_cast<TemplateViewItem*>(pItem);
917 
918     if( pViewItem )
919         maOpenTemplateHdl.Call(pViewItem);
920 }
921 
922 
923 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
924