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/templatedlglocalview.hxx>
11 
12 #include <comphelper/string.hxx>
13 #include <sfx2/inputdlg.hxx>
14 #include <templateviewitem.hxx>
15 #include <sfx2/sfxresid.hxx>
16 #include <templatecontaineritem.hxx>
17 #include <sfx2/strings.hrc>
18 #include <vcl/commandevent.hxx>
19 #include <vcl/svapp.hxx>
20 #include <vcl/event.hxx>
21 #include <sfx2/doctempl.hxx>
22 
TemplateDlgLocalView(std::unique_ptr<weld::ScrolledWindow> xWindow,std::unique_ptr<weld::Menu> xMenu,std::unique_ptr<weld::TreeView> xTreeView)23 TemplateDlgLocalView::TemplateDlgLocalView(std::unique_ptr<weld::ScrolledWindow> xWindow,
24                                            std::unique_ptr<weld::Menu> xMenu,
25                                            std::unique_ptr<weld::TreeView> xTreeView)
26     : TemplateLocalView(std::move(xWindow), std::move(xMenu))
27     , ListView(std::move(xTreeView))
28     , mViewMode(TemplateViewMode::eThumbnailView)
29 {
30     mxTreeView->connect_row_activated(LINK(this, TemplateDlgLocalView, RowActivatedHdl));
31     mxTreeView->connect_column_clicked(LINK(this, ListView, ColumnClickedHdl));
32     mxTreeView->connect_changed(LINK(this, TemplateDlgLocalView, ListViewChangedHdl));
33     mxTreeView->connect_popup_menu(LINK(this, TemplateDlgLocalView, PopupMenuHdl));
34     mxTreeView->connect_key_press(LINK(this, TemplateDlgLocalView, KeyPressHdl));
35 }
36 
showAllTemplates()37 void TemplateDlgLocalView::showAllTemplates()
38 {
39     mnCurRegionId = 0;
40 
41     insertItems(maAllTemplates, false, true);
42     insertFilteredItems();
43 
44     maOpenRegionHdl.Call(nullptr);
45 }
46 
showRegion(TemplateContainerItem const * pItem)47 void TemplateDlgLocalView::showRegion(TemplateContainerItem const* pItem)
48 {
49     mnCurRegionId = pItem->mnRegionId + 1;
50 
51     insertItems(pItem->maTemplates);
52     insertFilteredItems();
53 
54     maOpenRegionHdl.Call(nullptr);
55 }
56 
showRegion(std::u16string_view rName)57 void TemplateDlgLocalView::showRegion(std::u16string_view rName)
58 {
59     for (auto const& pRegion : maRegions)
60     {
61         if (pRegion->maTitle == rName)
62         {
63             showRegion(pRegion.get());
64             break;
65         }
66     }
67 }
68 
reload()69 void TemplateDlgLocalView::reload()
70 {
71     mpDocTemplates->Update();
72     OUString sCurRegionName = getRegionItemName(mnCurRegionId);
73     Populate();
74     mnCurRegionId = getRegionId(sCurRegionName);
75 
76     // Check if we are currently browsing a region or root folder
77     if (mnCurRegionId)
78     {
79         sal_uInt16 nRegionId = mnCurRegionId - 1; //Is offset by 1
80 
81         for (auto const& pRegion : maRegions)
82         {
83             if (pRegion->mnRegionId == nRegionId)
84             {
85                 showRegion(pRegion.get());
86                 break;
87             }
88         }
89     }
90     else
91         showAllTemplates();
92 
93     //No items should be selected by default
94     ThumbnailView::deselectItems();
95     ListView::unselect_all();
96 }
97 
createContextMenu(const bool bIsDefault,const bool bIsBuiltIn)98 void TemplateDlgLocalView::createContextMenu(const bool bIsDefault, const bool bIsBuiltIn)
99 {
100     mxContextMenu->clear();
101     mxContextMenu->append("open", SfxResId(STR_OPEN));
102     mxContextMenu->append("edit", SfxResId(STR_EDIT_TEMPLATE));
103 
104     if (!bIsDefault)
105         mxContextMenu->append("default", SfxResId(STR_DEFAULT_TEMPLATE));
106     else
107         mxContextMenu->append("default", SfxResId(STR_RESET_DEFAULT));
108 
109     mxContextMenu->append_separator("separator");
110     mxContextMenu->append("rename", SfxResId(STR_SFX_RENAME));
111     mxContextMenu->append("delete", SfxResId(STR_DELETE));
112     if (bIsBuiltIn)
113     {
114         mxContextMenu->set_sensitive("rename", false);
115         mxContextMenu->set_sensitive("edit", false);
116         mxContextMenu->set_sensitive("delete", false);
117     }
118     if (mViewMode == TemplateViewMode::eThumbnailView)
119     {
120         deselectItems();
121         maSelectedItem->setSelection(true);
122         maItemStateHdl.Call(maSelectedItem);
123         ContextMenuSelectHdl(mxContextMenu->popup_at_rect(
124             GetDrawingArea(), tools::Rectangle(maPosition, Size(1, 1))));
125         Invalidate();
126     }
127     else if (mViewMode == TemplateViewMode::eListView)
128         ContextMenuSelectHdl(mxContextMenu->popup_at_rect(
129             mxTreeView.get(), tools::Rectangle(maPosition, Size(1, 1))));
130 }
131 
ContextMenuSelectHdl(std::string_view rIdent)132 void TemplateDlgLocalView::ContextMenuSelectHdl(std::string_view rIdent)
133 {
134     if (rIdent == "open")
135         maOpenTemplateHdl.Call(maSelectedItem);
136     else if (rIdent == "edit")
137         maEditTemplateHdl.Call(maSelectedItem);
138     else if (rIdent == "rename")
139     {
140         InputDialog aTitleEditDlg(GetDrawingArea(), SfxResId(STR_RENAME_TEMPLATE));
141         OUString sOldTitle = maSelectedItem->getTitle();
142         aTitleEditDlg.SetEntryText(sOldTitle);
143         aTitleEditDlg.HideHelpBtn();
144 
145         auto aCurRegionItems = getFilteredItems([&](const TemplateItemProperties& rItem) {
146             return rItem.aRegionName == getRegionName(maSelectedItem->mnRegionId);
147         });
148         OUString sTooltip(SfxResId(STR_TOOLTIP_ERROR_RENAME_TEMPLATE));
149         sTooltip = sTooltip.replaceFirst("$2", getRegionName(maSelectedItem->mnRegionId));
150         aTitleEditDlg.setCheckEntry([&](OUString sNewTitle) {
151             if (sNewTitle.isEmpty() || sNewTitle == sOldTitle)
152                 return true;
153             for (const auto& rItem : aCurRegionItems)
154             {
155                 if (rItem.aName == sNewTitle)
156                 {
157                     aTitleEditDlg.SetTooltip(sTooltip.replaceFirst("$1", sNewTitle));
158                     return false;
159                 }
160             }
161             return true;
162         });
163         if (!aTitleEditDlg.run())
164             return;
165         OUString sNewTitle = comphelper::string::strip(aTitleEditDlg.GetEntryText(), ' ');
166 
167         if (!sNewTitle.isEmpty() && sNewTitle != sOldTitle)
168         {
169             maSelectedItem->setTitle(sNewTitle);
170             ListView::rename(OUString::number(maSelectedItem->mnId), maSelectedItem->maTitle);
171         }
172     }
173     else if (rIdent == "delete")
174     {
175         std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(
176             GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo,
177             SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE)));
178         if (xQueryDlg->run() != RET_YES)
179             return;
180 
181         maDeleteTemplateHdl.Call(maSelectedItem);
182         reload();
183     }
184     else if (rIdent == "default")
185     {
186         maDefaultTemplateHdl.Call(maSelectedItem);
187         ListView::refreshDefaultColumn();
188     }
189 }
190 
insertFilteredItems()191 void TemplateDlgLocalView::insertFilteredItems()
192 {
193     ListView::clearListView();
194     for (const ThumbnailViewItem* rItem : mFilteredItemList)
195     {
196         const TemplateViewItem* pViewItem = static_cast<const TemplateViewItem*>(rItem);
197         if (!pViewItem)
198             return;
199         bool isDefault = pViewItem->IsDefaultTemplate();
200         OUString sId = OUString::number(pViewItem->mnId);
201         ListView::AppendItem(sId, rItem->maTitle, getRegionName(pViewItem->mnRegionId),
202                              pViewItem->getPath(), isDefault);
203     }
204     ListView::sort();
205 }
206 
setTemplateViewMode(TemplateViewMode eMode)207 void TemplateDlgLocalView::setTemplateViewMode(TemplateViewMode eMode) { mViewMode = eMode; }
208 
Show()209 void TemplateDlgLocalView::Show()
210 {
211     if (mViewMode == TemplateViewMode::eListView)
212     {
213         ThumbnailView::Hide();
214         ListView::ShowListView();
215     }
216     else
217     {
218         ThumbnailView::Show();
219         ListView::HideListView();
220     }
221     syncCursor();
222 }
Hide()223 void TemplateDlgLocalView::Hide()
224 {
225     ThumbnailView::Hide();
226     ListView::HideListView();
227 }
228 
IsVisible() const229 bool TemplateDlgLocalView::IsVisible() const
230 {
231     return ThumbnailView::IsVisible() || ListView::IsListViewVisible();
232 }
233 
syncCursor()234 void TemplateDlgLocalView::syncCursor()
235 {
236     if (mViewMode == TemplateViewMode::eListView)
237     {
238         ListView::unselect_all();
239         int nIndex = -1;
240 
241         for (auto it = mFilteredItemList.cbegin(); it != mFilteredItemList.cend(); ++it)
242         {
243             if ((*it)->mbSelected)
244             {
245                 nIndex = -1;
246                 nIndex = ListView::get_index((*it)->mnId);
247                 if (nIndex >= 0)
248                 {
249                     ListView::set_cursor(nIndex);
250                     ListView::select(nIndex);
251                     break;
252                 }
253             }
254         }
255         updateSelection();
256     }
257     else
258     {
259         ThumbnailView::deselectItems();
260         std::vector<int> aSelRows = ListView::get_selected_rows();
261         if (aSelRows.empty())
262             return;
263         sal_uInt16 nCursorId = ListView::get_cursor_nId();
264         ThumbnailView::SelectItem(nCursorId);
265         MakeItemVisible(nCursorId);
266 
267         for (auto it = mFilteredItemList.begin(); it != mFilteredItemList.end(); ++it)
268         {
269             if ((*it)->mnId == nCursorId)
270             {
271                 mpStartSelRange = it;
272                 break;
273             }
274         }
275 
276         size_t nPos = GetItemPos(nCursorId);
277         ThumbnailViewItem* pItem = ImplGetItem(nPos);
278         const TemplateViewItem* pViewItem = dynamic_cast<const TemplateViewItem*>(pItem);
279         if (pViewItem)
280             maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem);
281     }
282 }
283 
updateSelection()284 void TemplateDlgLocalView::updateSelection()
285 {
286     ThumbnailView::deselectItems();
287     for (auto nIndex : ListView::get_selected_rows())
288     {
289         ThumbnailView::SelectItem(ListView::get_nId(nIndex));
290     }
291 
292     sal_uInt16 nCursorId = ListView::get_cursor_nId();
293     size_t nPos = GetItemPos(nCursorId);
294     ThumbnailViewItem* pItem = ImplGetItem(nPos);
295     const TemplateViewItem* pViewItem = dynamic_cast<const TemplateViewItem*>(pItem);
296     if (pViewItem)
297         maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem);
298     return;
299 }
300 
IMPL_LINK_NOARG(TemplateDlgLocalView,RowActivatedHdl,weld::TreeView &,bool)301 IMPL_LINK_NOARG(TemplateDlgLocalView, RowActivatedHdl, weld::TreeView&, bool)
302 {
303     maOpenTemplateHdl.Call(maSelectedItem);
304     return true;
305 }
306 
IMPL_LINK(TemplateDlgLocalView,PopupMenuHdl,const CommandEvent &,rCEvt,bool)307 IMPL_LINK(TemplateDlgLocalView, PopupMenuHdl, const CommandEvent&, rCEvt, bool)
308 {
309     if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
310         return false;
311 
312     if (rCEvt.IsMouseEvent())
313     {
314         if (ListView::get_selected_rows().empty())
315             return true;
316         int nIndex = ListView::get_cursor_index();
317         ListView::unselect_all();
318         ListView::select(nIndex);
319         ListView::set_cursor(nIndex);
320         Point aPosition(rCEvt.GetMousePosPixel());
321         maPosition = aPosition;
322         updateSelection();
323         if (maSelectedItem)
324             maCreateContextMenuHdl.Call(maSelectedItem);
325         return true;
326     }
327     else
328     {
329         if (ListView::get_selected_rows().empty())
330             return true;
331         int nIndex = ListView::get_cursor_index();
332         ListView::unselect_all();
333         ListView::select(nIndex);
334         ListView::set_cursor(nIndex);
335         maPosition = Point(0, 0);
336         updateSelection();
337         if (maSelectedItem)
338             maCreateContextMenuHdl.Call(maSelectedItem);
339         return true;
340     }
341 }
342 
IMPL_LINK_NOARG(TemplateDlgLocalView,ListViewChangedHdl,weld::TreeView &,void)343 IMPL_LINK_NOARG(TemplateDlgLocalView, ListViewChangedHdl, weld::TreeView&, void)
344 {
345     updateSelection();
346 }
347 
KeyInput(const KeyEvent & rKEvt)348 bool TemplateDlgLocalView::KeyInput(const KeyEvent& rKEvt)
349 {
350     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
351 
352     if (aKeyCode == (KEY_MOD1 | KEY_A))
353     {
354         for (ThumbnailViewItem* pItem : mFilteredItemList)
355         {
356             if (!pItem->isSelected())
357             {
358                 pItem->setSelection(true);
359                 maItemStateHdl.Call(pItem);
360             }
361         }
362 
363         if (IsReallyVisible() && IsUpdateMode())
364             Invalidate();
365         return true;
366     }
367     else if (aKeyCode == KEY_DELETE && !mFilteredItemList.empty())
368     {
369         std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(
370             GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo,
371             SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE)));
372         if (xQueryDlg->run() != RET_YES)
373             return true;
374 
375         //copy to avoid changing filtered item list during deletion
376         ThumbnailValueItemList mFilteredItemListCopy = mFilteredItemList;
377 
378         for (ThumbnailViewItem* pItem : mFilteredItemListCopy)
379         {
380             if (pItem->isSelected())
381             {
382                 maDeleteTemplateHdl.Call(pItem);
383             }
384         }
385         reload();
386     }
387 
388     return ThumbnailView::KeyInput(rKEvt);
389 }
390 
IMPL_LINK(TemplateDlgLocalView,KeyPressHdl,const KeyEvent &,rKEvt,bool)391 IMPL_LINK(TemplateDlgLocalView, KeyPressHdl, const KeyEvent&, rKEvt, bool)
392 {
393     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
394 
395     if (aKeyCode == KEY_DELETE && !mFilteredItemList.empty()
396         && !ListView::get_selected_rows().empty())
397     {
398         std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog(
399             mxTreeView.get(), VclMessageType::Question, VclButtonsType::YesNo,
400             SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE)));
401         if (xQueryDlg->run() != RET_YES)
402             return true;
403 
404         //copy to avoid changing filtered item list during deletion
405         ThumbnailValueItemList mFilteredItemListCopy = mFilteredItemList;
406 
407         for (ThumbnailViewItem* pItem : mFilteredItemListCopy)
408         {
409             if (pItem->isSelected())
410             {
411                 maDeleteTemplateHdl.Call(pItem);
412             }
413         }
414 
415         reload();
416     }
417     return false;
418 }
419 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
420