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