diff options
author | Vert D <devoptmsoi@gmx.com> | 2020-09-25 17:58:15 -0500 |
---|---|---|
committer | Heiko Tietze <heiko.tietze@documentfoundation.org> | 2020-11-23 11:29:04 +0100 |
commit | 6b1de6057082bd8720594231839f967bff5372ae (patch) | |
tree | 57ae7c1982f3b270e3a42dec0bff909f6e250410 | |
parent | 3c4b09a250605c9d73e48df83a6ee36a8bc2fb8b (diff) |
tdf#104154 WIP:Add list view to template manager
*Added Thumbnail View and List View Buttons,
*selection is remembered for the next launch of the template manager.
*List view added to local view and search view.
*Added columns: name, category, application, modified, size and path.
*Added column sorting.
*Search, move, set as default and other existing tasks.
Change-Id: I7615f7e41020916ae518b639dba915a0a9340ff5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103418
Tested-by: Jenkins
Tested-by: Heiko Tietze <heiko.tietze@documentfoundation.org>
Reviewed-by: Heiko Tietze <heiko.tietze@documentfoundation.org>
-rw-r--r-- | include/sfx2/listview.hxx | 118 | ||||
-rw-r--r-- | include/sfx2/strings.hrc | 5 | ||||
-rw-r--r-- | include/sfx2/templatedlg.hxx | 17 | ||||
-rw-r--r-- | include/sfx2/templatedlglocalview.hxx | 62 | ||||
-rw-r--r-- | sfx2/Library_sfx.mk | 2 | ||||
-rw-r--r-- | sfx2/source/control/listview.cxx | 471 | ||||
-rw-r--r-- | sfx2/source/control/templatedlglocalview.cxx | 294 | ||||
-rw-r--r-- | sfx2/source/control/templatesearchview.cxx | 182 | ||||
-rw-r--r-- | sfx2/source/doc/templatedlg.cxx | 114 | ||||
-rw-r--r-- | sfx2/source/inc/templatesearchview.hxx | 33 | ||||
-rw-r--r-- | sfx2/uiconfig/ui/templatedlg.ui | 316 |
11 files changed, 1591 insertions, 23 deletions
diff --git a/include/sfx2/listview.hxx b/include/sfx2/listview.hxx new file mode 100644 index 000000000000..0c5714caca33 --- /dev/null +++ b/include/sfx2/listview.hxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <vcl/weld.hxx> + +enum TemplateViewMode +{ + eListView, + eThumbnailView +}; +class SfxDocumentTemplates; +class TemplateContainerItem; +struct ListViewItem; + +class ListView +{ +public: + ListView(std::unique_ptr<weld::TreeView> xTreeView); + ~ListView(); + + void AppendItem(const OUString& rId, const OUString& rTitle, const OUString& rSubtitle, + const OUString& rPath, bool bDefault); + + void AppendRow(const OUString& rImage, const OUString& rTitle, const OUString& rSubtitle, + const OUString& rApplication, const OUString& rModify, const OUString& rSize, + const OUString& rId); + + void UpdateRow(int nIndex, const OUString& rImage, const OUString& rTitle, + const OUString& rSubtitle, const OUString& rApplication, const OUString& rModify, + const OUString& rSize, const OUString& rId); + + void ReloadRows(); + + bool UpdateRows(); + + void sortColumn(const int col); + + void sort(); + + void clearListView(); + + void ShowListView() { mxTreeView->show(); } + + void HideListView() { mxTreeView->hide(); } + + void unselect_all() { mxTreeView->unselect_all(); } + + void remove(const OUString& rId); + + void rename(const OUString& rId, const OUString& rTitle); + + void refreshDefaultColumn(); + +protected: + sal_uInt16 get_nId(int pos); + + OUString get_selected_id() { return mxTreeView->get_selected_id(); } + + void select_id(const OUString& sId) { mxTreeView->select_id(sId); } + + int get_selected_index() { return mxTreeView->get_selected_index(); } + + std::vector<int> get_selected_rows() { return mxTreeView->get_selected_rows(); } + + bool IsListViewVisible() { return mxTreeView->is_visible(); } + + OUString get_id(int pos) { return mxTreeView->get_id(pos); } + + void set_cursor(int pos) { mxTreeView->set_cursor(pos); } + + int get_cursor_index() { return mxTreeView->get_cursor_index(); } + + sal_uInt16 get_cursor_nId() { return get_nId(mxTreeView->get_cursor_index()); } + + void select(int pos) { mxTreeView->select(pos); } + + int get_index(sal_uInt16 nId) { return mxTreeView->find_id(OUString::number(nId)); } + + DECL_LINK(ColumnClickedHdl, const int, void); + + DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString); + +protected: + std::unique_ptr<weld::TreeView> mxTreeView; + std::vector<std::unique_ptr<ListViewItem>> mListViewItems; + Link<weld::TreeView&, void> maSelectionChangedHdl; + int mnSortColumn; +}; + +struct ListViewItem +{ +public: + OUString maId; + OUString maTitle; + OUString maSubtitle; + OUString maApplication; + OUString maPath; + bool mbDefault; + + /** Last modify time in seconds since 1/1/1970. */ + sal_uInt32 mnModify; + /** Size in bytes of the file. */ + sal_uInt64 mnSize; + + OUString maDisplayModify; + OUString maDisplaySize; + OUString maDisplayPath; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/sfx2/strings.hrc b/include/sfx2/strings.hrc index cb627807d8c8..9927afe17e93 100644 --- a/include/sfx2/strings.hrc +++ b/include/sfx2/strings.hrc @@ -343,6 +343,11 @@ #define STR_CLICKHYPERLINK NC_("STR_CLICKHYPERLINK", "Click to open hyperlink: %{link}") #define STR_STYLEUSEDBY NC_("STR_STYLEUSEDBY", "(used by: %STYLELIST)") + +#define STR_DOCUMENT NC_("STR_DOCUMENT", "Document") +#define STR_SPREADSHEET NC_("STR_SPREADSHEET", "Spreadsheet") +#define STR_PRESENTATION NC_("STR_PRESENTATION", "Presentation") +#define STR_DRAWING NC_("STR_DRAWING", "Drawing") #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/sfx2/templatedlg.hxx b/include/sfx2/templatedlg.hxx index 76d7d98eced5..e9cf26d2cb8a 100644 --- a/include/sfx2/templatedlg.hxx +++ b/include/sfx2/templatedlg.hxx @@ -19,7 +19,7 @@ #include <vcl/timer.hxx> #include <vcl/weld.hxx> -#include <sfx2/templatelocalview.hxx> +#include <sfx2/templatedlglocalview.hxx> class TemplateSearchView; class ThumbnailViewItem; @@ -44,6 +44,8 @@ public: virtual short run() override; void setDocumentModel(const css::uno::Reference<css::frame::XModel>& rModel); + void setTemplateViewMode(TemplateViewMode eViewMode); + TemplateViewMode getTemplateViewMode(); protected: void getApplicationSpecificSettings(); @@ -83,6 +85,11 @@ protected: DECL_LINK(ImplUpdateDataHdl, Timer*, void); DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(ListViewHdl, weld::Button&, void); + DECL_LINK(ThumbnailViewHdl, weld::Button&, void); + DECL_LINK(FocusRectLocalHdl, weld::Widget&, tools::Rectangle); + DECL_LINK(FocusRectSearchHdl, weld::Widget&, tools::Rectangle); + void OnTemplateImportCategory(const OUString& sCategory); // static void OnTemplateLink (); void OnTemplateOpen(); @@ -134,10 +141,14 @@ protected: std::unique_ptr<weld::CheckButton> mxCBXHideDlg; std::unique_ptr<weld::MenuButton> mxActionBar; std::unique_ptr<TemplateSearchView> mxSearchView; - std::unique_ptr<TemplateLocalView> mxLocalView; + std::unique_ptr<TemplateDlgLocalView> mxLocalView; std::unique_ptr<weld::Menu> mxTemplateDefaultMenu; std::unique_ptr<weld::CustomWeld> mxSearchViewWeld; std::unique_ptr<weld::CustomWeld> mxLocalViewWeld; + std::unique_ptr<weld::ToggleButton> mxListViewButton; + std::unique_ptr<weld::ToggleButton> mxThumbnailViewButton; + TemplateViewMode mViewMode; + bool bMakeSelItemVisible; }; // class SfxTemplateCategoryDialog ------------------------------------------------------------------- @@ -198,4 +209,4 @@ private: #endif // INCLUDED_SFX2_INC_TEMPLATEDLG_HXX -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file diff --git a/include/sfx2/templatedlglocalview.hxx b/include/sfx2/templatedlglocalview.hxx new file mode 100644 index 000000000000..a8fc57e254c3 --- /dev/null +++ b/include/sfx2/templatedlglocalview.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include <sfx2/templatelocalview.hxx> +#include <sfx2/listview.hxx> + +class TemplateDlgLocalView final : public TemplateLocalView, public ListView +{ +public: + TemplateDlgLocalView(std::unique_ptr<weld::ScrolledWindow> xWindow, + std::unique_ptr<weld::Menu> xMenu, + std::unique_ptr<weld::TreeView> xTreeView); + + void setTemplateViewMode(TemplateViewMode eMode); + + virtual void showAllTemplates() override; + + void showRegion(TemplateContainerItem const* pItem); + + void showRegion(const OUString& rName); + + void createContextMenu(const bool bIsDefault); + + virtual void Show() override; + + virtual void Hide() override; + + bool IsVisible(); + + void connect_focus_rect(const Link<weld::Widget&, tools::Rectangle>& rLink) + { + GetDrawingArea()->connect_focus_rect(rLink); + } + + void MakeItemVisible(sal_uInt16 nId) { ThumbnailView::MakeItemVisible(nId); } + +private: + void ContextMenuSelectHdl(const OString& rIdent); + + void insertFilteredItems(); + + void syncCursor(); + + void updateSelection(); + + DECL_LINK(RowActivatedHdl, weld::TreeView&, bool); + + DECL_LINK(ListViewChangedHdl, weld::TreeView&, void); + + DECL_LINK(PopupMenuHdl, const CommandEvent&, bool); + + TemplateViewMode mViewMode; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/Library_sfx.mk b/sfx2/Library_sfx.mk index cf3eaed6f81a..b7f69f1d8e5b 100644 --- a/sfx2/Library_sfx.mk +++ b/sfx2/Library_sfx.mk @@ -150,6 +150,8 @@ $(eval $(call gb_Library_add_exception_objects,sfx,\ sfx2/source/control/templatelocalview \ sfx2/source/control/templatecontaineritem \ sfx2/source/control/templatesearchview \ + sfx2/source/control/templatedlglocalview \ + sfx2/source/control/listview \ sfx2/source/control/thumbnailviewitem \ sfx2/source/control/thumbnailviewacc \ sfx2/source/control/thumbnailview \ diff --git a/sfx2/source/control/listview.cxx b/sfx2/source/control/listview.cxx new file mode 100644 index 000000000000..93e9678cc6f5 --- /dev/null +++ b/sfx2/source/control/listview.cxx @@ -0,0 +1,471 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <vcl/headbar.hxx> +#include <sfx2/listview.hxx> + +#include <sfx2/doctempl.hxx> +#include <sfx2/sfxresid.hxx> +#include <tools/urlobj.hxx> +#include <vcl/svapp.hxx> + +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> + +#include <sfx2/strings.hrc> +#include <osl/file.hxx> +#include <osl/time.h> +#include <comphelper/fileurl.hxx> +#include <string> + +#include <svtools/svtresid.hxx> +#include <svtools/strings.hrc> +#include <unotools/localedatawrapper.hxx> +#include <unotools/collatorwrapper.hxx> +#include <unotools/syslocale.hxx> +#include <unotools/intlwrapper.hxx> + +#include <sfx2/strings.hrc> +#include <bitmaps.hlst> +#include <rtl/math.hxx> + +#include <sfx2/templatelocalview.hxx> + +#define COLUMN_IMG_ISDEFAULT 0 +#define COLUMN_NAME 1 +#define COLUMN_CATEGORY 2 +#define COLUMN_APPLICATION 3 +#define COLUMN_MODIFIED 4 +#define COLUMN_SIZE 5 +#define NUMBER_OF_COLUMNS 6 + +static sal_uInt64 getFileSize(const OUString& rURL); +static sal_uInt32 getFileModifyTime(const OUString& rURL); +static OUString getDisplayFileSize(const OUString& rURL); +static OUString getDisplayFileModifyTime(const OUString& rURL); +static OUString getApplication(const OUString& rURL); + +ListView::ListView(std::unique_ptr<weld::TreeView> xTreeView) + : mxTreeView(std::move(xTreeView)) + , mnSortColumn(-2) +{ + auto nDigitWidth = mxTreeView->get_approximate_digit_width(); + std::vector<int> aWidths; + aWidths.push_back(static_cast<int>(nDigitWidth * 5)); /* Icon Column */ + aWidths.push_back(static_cast<int>(nDigitWidth * 24)); /* Name Column */ + aWidths.push_back(static_cast<int>(nDigitWidth * 22)); /* Category Column */ + aWidths.push_back(static_cast<int>(nDigitWidth * 15)); /* Application Column */ + aWidths.push_back(static_cast<int>(nDigitWidth * 18)); /* Modify Column */ + aWidths.push_back(static_cast<int>(nDigitWidth * 10)); /* Size Column */ + + mxTreeView->set_column_fixed_widths(aWidths); + mxTreeView->set_selection_mode(SelectionMode::Multiple); + mxTreeView->connect_query_tooltip(LINK(this, ListView, QueryTooltipHdl)); +} +ListView::~ListView() {} + +void ListView::AppendItem(const OUString& rId, const OUString& rTitle, const OUString& rSubtitle, + const OUString& rPath, bool bDefault) +{ + INetURLObject aUrl(rPath, INetProtocol::File); + OUString sPath = aUrl.getFSysPath(FSysStyle::Detect); + + std::unique_ptr<ListViewItem> pItem(new ListViewItem); + pItem->maId = rId; + pItem->maTitle = rTitle; + pItem->maSubtitle = rSubtitle; + pItem->maApplication = getApplication(rPath); + pItem->maPath = rPath; + pItem->mbDefault = bDefault; + pItem->mnModify = getFileModifyTime(rPath); + pItem->mnSize = getFileSize(rPath); + pItem->maDisplayModify = getDisplayFileModifyTime(rPath); + pItem->maDisplaySize = getDisplayFileSize(rPath); + pItem->maDisplayPath = sPath; + + OUString sImage(""); + if (pItem->mbDefault) + sImage = BMP_DEFAULT; + + AppendRow(sImage, pItem->maTitle, pItem->maSubtitle, pItem->maApplication, + pItem->maDisplayModify, pItem->maDisplaySize, pItem->maId); + + mListViewItems.push_back(std::move(pItem)); +} + +void ListView::AppendRow(const OUString& rImage, const OUString& rTitle, const OUString& rSubtitle, + const OUString& rApplication, const OUString& rModify, + const OUString& rSize, const OUString& rId) +{ + std::unique_ptr<weld::TreeIter> xIter(mxTreeView->make_iterator()); + mxTreeView->append(xIter.get()); + mxTreeView->set_image(*xIter, rImage, COLUMN_IMG_ISDEFAULT); + mxTreeView->set_text(*xIter, rTitle, COLUMN_NAME); + mxTreeView->set_text(*xIter, rSubtitle, COLUMN_CATEGORY); + mxTreeView->set_text(*xIter, rApplication, COLUMN_APPLICATION); + mxTreeView->set_text(*xIter, rModify, COLUMN_MODIFIED); + mxTreeView->set_text(*xIter, rSize, COLUMN_SIZE); + mxTreeView->set_id(*xIter, rId); +} + +void ListView::UpdateRow(int nIndex, const OUString& rImage, const OUString& rTitle, + const OUString& rSubtitle, const OUString& rApplication, + const OUString& rModify, const OUString& rSize, const OUString& rId) +{ + mxTreeView->set_image(nIndex, rImage, COLUMN_IMG_ISDEFAULT); + mxTreeView->set_text(nIndex, rTitle, COLUMN_NAME); + mxTreeView->set_text(nIndex, rSubtitle, COLUMN_CATEGORY); + mxTreeView->set_text(nIndex, rApplication, COLUMN_APPLICATION); + mxTreeView->set_text(nIndex, rModify, COLUMN_MODIFIED); + mxTreeView->set_text(nIndex, rSize, COLUMN_SIZE); + mxTreeView->set_id(nIndex, rId); +} + +void ListView::ReloadRows() +{ + OUString sCursorId = get_id(get_cursor_index()); + mxTreeView->clear(); + for (const auto& pItem : mListViewItems) + { + OUString sImage(""); + if (pItem->mbDefault) + sImage = BMP_DEFAULT; + AppendRow(sImage, pItem->maTitle, pItem->maSubtitle, pItem->maApplication, + pItem->maDisplayModify, pItem->maDisplaySize, pItem->maId); + } + unselect_all(); + if (!sCursorId.isEmpty()) + { + select_id(sCursorId); + set_cursor(get_selected_index()); + } +} + +bool ListView::UpdateRows() +{ + if (static_cast<int>(mListViewItems.size()) != mxTreeView->n_children()) + return false; + OUString sCursorId = get_id(get_cursor_index()); + int nIndex = 0; + for (const auto& pItem : mListViewItems) + { + OUString sImage(""); + if (pItem->mbDefault) + sImage = BMP_DEFAULT; + UpdateRow(nIndex, sImage, pItem->maTitle, pItem->maSubtitle, pItem->maApplication, + pItem->maDisplayModify, pItem->maDisplaySize, pItem->maId); + ++nIndex; + } + unselect_all(); + if (!sCursorId.isEmpty()) + { + select_id(sCursorId); + set_cursor(get_selected_index()); + } + return true; +} + +IMPL_LINK(ListView, ColumnClickedHdl, const int, col, void) +{ + if (col <= 0 || col > NUMBER_OF_COLUMNS) + return; + + if (mnSortColumn >= 0 && mnSortColumn != col) + mxTreeView->set_sort_indicator(TriState::TRISTATE_INDET, mnSortColumn); + + mxTreeView->set_sort_indicator((mxTreeView->get_sort_indicator(col) == TriState::TRISTATE_TRUE + ? TriState::TRISTATE_FALSE + : TriState::TRISTATE_TRUE), + col); + sortColumn(col); +} + +void ListView::sortColumn(const int col) +{ + if (col <= 0 || col > NUMBER_OF_COLUMNS) + return; + + bool isAscending = mxTreeView->get_sort_indicator(col) != TriState::TRISTATE_FALSE; + + auto comp = [&](std::unique_ptr<ListViewItem> const& pItemA, + std::unique_ptr<ListViewItem> const& pItemB) { + sal_Int32 res = 0; + IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag()); + const CollatorWrapper* pCollatorWrapper = aIntlWrapper.getCollator(); + switch (col) + { + case COLUMN_NAME: + { + OUString sNameA = pItemA->maTitle; + OUString sNameB = pItemB->maTitle; + res = pCollatorWrapper->compareString(sNameA, sNameB); + } + break; + case COLUMN_CATEGORY: + { + OUString sCategoryA = pItemA->maSubtitle; + OUString sCategoryB = pItemB->maSubtitle; + res = pCollatorWrapper->compareString(sCategoryA, sCategoryB); + } + break; + case COLUMN_MODIFIED: + { + sal_uInt32 nModA, nModB; + nModA = pItemA->mnModify; + nModB = pItemB->mnModify; + + if (nModA < nModB) + res = -1; + else if (nModA > nModB) + res = 1; + } + break; + case COLUMN_SIZE: + { + sal_uInt64 nSizeA, nSizeB; + nSizeA = pItemA->mnSize; + nSizeB = pItemB->mnSize; + + if (nSizeA < nSizeB) + res = -1; + else if (nSizeA > nSizeB) + res = 1; + } + break; + case COLUMN_APPLICATION: + { + OUString sPathA = pItemA->maApplication; + OUString sPathB = pItemB->maApplication; + res = pCollatorWrapper->compareString(sPathA, sPathB); + } + break; + } + return isAscending ? (res > 0) : (res < 0); + }; + std::stable_sort(mListViewItems.begin(), mListViewItems.end(), comp); + + if (!UpdateRows()) + ReloadRows(); + mnSortColumn = col; +} + +void ListView::sort() { sortColumn(mnSortColumn); } + +void ListView::refreshDefaultColumn() +{ + for (const auto& pItem : mListViewItems) + { + bool bDefault = TemplateLocalView::IsDefaultTemplate(pItem->maPath); + if (pItem->mbDefault != bDefault) + { + pItem->mbDefault = bDefault; + OUString sImage(""); + if (bDefault) + sImage = BMP_DEFAULT; + mxTreeView->set_image(mxTreeView->find_id(pItem->maId), sImage, COLUMN_IMG_ISDEFAULT); + } + } +} + +void ListView::rename(const OUString& rId, const OUString& rTitle) +{ + mxTreeView->set_text(mxTreeView->find_id(rId), rTitle, COLUMN_NAME); + for (const auto& pItem : mListViewItems) + if (pItem->maId == rId) + { + pItem->maTitle = rTitle; + break; + } +} + +void ListView::remove(const OUString& rId) +{ + mxTreeView->remove_id(rId); + for (auto it = mListViewItems.begin(); it != mListViewItems.end(); ++it) + if ((*it)->maId == rId) + { + mListViewItems.erase(it); + break; + } +} + +void ListView::clearListView() +{ + mxTreeView->clear(); + mListViewItems.clear(); +} + +IMPL_LINK(ListView, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString) +{ + OUString sId = mxTreeView->get_id(rIter); + for (auto& pItem : mListViewItems) + { + if (pItem->maId == sId) + return pItem->maDisplayPath; + } + return OUString(); +} + +sal_uInt16 ListView::get_nId(int pos) +{ + return static_cast<sal_uInt16>(mxTreeView->get_id(pos).toInt32()); +} + +static sal_uInt32 getFileModifyTime(const OUString& rURL) +{ + sal_uInt32 nModify = 0; + if (!comphelper::isFileUrl(rURL)) + return nModify; + + osl::DirectoryItem aItem; + if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None) + return nModify; + + osl::FileStatus aStatus(osl_FileStatus_Mask_ModifyTime); + if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None) + return nModify; + + TimeValue systemTimeValue = aStatus.getModifyTime(); + + nModify = systemTimeValue.Seconds; + return nModify; +} +static OUString getDisplayFileModifyTime(const OUString& rURL) +{ + if (!comphelper::isFileUrl(rURL)) + return OUString(); + + osl::DirectoryItem aItem; + if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None) + return OUString(); + + osl::FileStatus aStatus(osl_FileStatus_Mask_ModifyTime); + if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None) + return OUString(); + + TimeValue systemTimeValue = aStatus.getModifyTime(); + if (systemTimeValue.Seconds == 0) + return OUString(); + TimeValue localTimeValue; + oslDateTime dateTime; + osl_getLocalTimeFromSystemTime(&systemTimeValue, &localTimeValue); + osl_getDateTimeFromTimeValue(&localTimeValue, &dateTime); + + struct tm tm; + tm.tm_sec = dateTime.Seconds; + tm.tm_min = dateTime.Minutes; + tm.tm_hour = dateTime.Hours; + tm.tm_mday = dateTime.Day; + tm.tm_mon = dateTime.Month - 1; + tm.tm_year = dateTime.Year - 1900; + char ts[50]; + for (char& c : ts) + c = ' '; + strftime(ts, sizeof(ts), "%x %X", &tm); + OUString sModifyTime(ts, sizeof(ts), RTL_TEXTENCODING_UTF8); + return sModifyTime.trim(); +} + +static OUString getDisplayFileSize(const OUString& rURL) +{ + if (!comphelper::isFileUrl(rURL)) + return OUString(); + + osl::DirectoryItem aItem; + if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None) + return OUString(); + + osl::FileStatus aStatus(osl_FileStatus_Mask_FileSize); + if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None) + return OUString(); + + sal_uInt64 nSize = aStatus.getFileSize(); + double fSize(static_cast<double>(nSize)); + sal_uInt32 nDec; + + sal_uInt64 nMega = 1024 * 1024; + sal_uInt64 nGiga = nMega * 1024; + + OUString aUnitStr(' '); + + if (nSize < 10000) + { + aUnitStr += SvtResId(STR_SVT_BYTES); + nDec = 0; + } + else if (nSize < nMega) + { + fSize /= 1024; + aUnitStr += SvtResId(STR_SVT_KB); + nDec = 1; + } + else if (nSize < nGiga) + { + fSize /= nMega; + aUnitStr += SvtResId(STR_SVT_MB); + nDec = 2; + } + else + { + fSize /= nGiga; + aUnitStr += SvtResId(STR_SVT_GB); + nDec = 3; + } + + OUString aSizeStr( + ::rtl::math::doubleToUString(fSize, rtl_math_StringFormat_F, nDec, + SvtSysLocale().GetLocaleData().getNumDecimalSep()[0])); + aSizeStr += aUnitStr; + + return aSizeStr; +} + +static sal_uInt64 getFileSize(const OUString& rURL) +{ + sal_uInt64 nSize = 0; + if (!comphelper::isFileUrl(rURL)) + return nSize; + + osl::DirectoryItem aItem; + if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None) + return nSize; + + osl::FileStatus aStatus(osl_FileStatus_Mask_FileSize); + if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None) + return nSize; + + nSize = aStatus.getFileSize(); + return nSize; +} + +static OUString getApplication(const OUString& rURL) +{ + INetURLObject aUrl(rURL); + OUString aExt = aUrl.getExtension(); + + if (aExt == "ott" || aExt == "stw" || aExt == "oth" || aExt == "dot" || aExt == "dotx") + { + return SfxResId(STR_DOCUMENT); + } + else if (aExt == "ots" || aExt == "stc" || aExt == "xlt" || aExt == "xltm" || aExt == "xltx") + { + return SfxResId(STR_SPREADSHEET); + } + else if (aExt == "otp" || aExt == "sti" || aExt == "pot" || aExt == "potm" || aExt == "potx") + { + return SfxResId(STR_PRESENTATION); + } + else if (aExt == "otg" || aExt == "std") + { + return SfxResId(STR_DRAWING); + } + return OUString(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/control/templatedlglocalview.cxx b/sfx2/source/control/templatedlglocalview.cxx new file mode 100644 index 000000000000..84aa6cdca363 --- /dev/null +++ b/sfx2/source/control/templatedlglocalview.cxx @@ -0,0 +1,294 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sfx2/templatedlglocalview.hxx> + +#include <comphelper/string.hxx> +#include <sfx2/inputdlg.hxx> +#include <templateviewitem.hxx> +#include <sfx2/doctempl.hxx> +#include <sfx2/sfxresid.hxx> +#include <templatecontaineritem.hxx> +#include <sfx2/strings.hrc> +#include <vcl/commandevent.hxx> +#include <vcl/svapp.hxx> + +TemplateDlgLocalView::TemplateDlgLocalView(std::unique_ptr<weld::ScrolledWindow> xWindow, + std::unique_ptr<weld::Menu> xMenu, + std::unique_ptr<weld::TreeView> xTreeView) + : TemplateLocalView(std::move(xWindow), std::move(xMenu)) + , ListView(std::move(xTreeView)) + , mViewMode(TemplateViewMode::eThumbnailView) +{ + mxTreeView->connect_row_activated(LINK(this, TemplateDlgLocalView, RowActivatedHdl)); + mxTreeView->connect_column_clicked(LINK(this, ListView, ColumnClickedHdl)); + mxTreeView->connect_changed(LINK(this, TemplateDlgLocalView, ListViewChangedHdl)); + mxTreeView->connect_popup_menu(LINK(this, TemplateDlgLocalView, PopupMenuHdl)); +} + +void TemplateDlgLocalView::showAllTemplates() +{ + mnCurRegionId = 0; + + insertItems(maAllTemplates, false, true); + insertFilteredItems(); + + maOpenRegionHdl.Call(nullptr); +} + +void TemplateDlgLocalView::showRegion(TemplateContainerItem const* pItem) +{ + mnCurRegionId = pItem->mnRegionId + 1; + + insertItems(pItem->maTemplates); + insertFilteredItems(); + + maOpenRegionHdl.Call(nullptr); +} + +void TemplateDlgLocalView::showRegion(const OUString& rName) +{ + for (auto const& pRegion : maRegions) + { + if (pRegion->maTitle == rName) + { + showRegion(pRegion.get()); + break; + } + } +} + +void TemplateDlgLocalView::createContextMenu(const bool bIsDefault) +{ + mxContextMenu->clear(); + mxContextMenu->append("open", SfxResId(STR_OPEN)); + mxContextMenu->append("edit", SfxResId(STR_EDIT_TEMPLATE)); + + if (!bIsDefault) + mxContextMenu->append("default", SfxResId(STR_DEFAULT_TEMPLATE)); + else + mxContextMenu->append("default", SfxResId(STR_RESET_DEFAULT)); + + mxContextMenu->append_separator("separator"); + mxContextMenu->append("rename", SfxResId(STR_SFX_RENAME)); + mxContextMenu->append("delete", SfxResId(STR_DELETE)); + if (mViewMode == TemplateViewMode::eThumbnailView) + { + deselectItems(); + maSelectedItem->setSelection(true); + maItemStateHdl.Call(maSelectedItem); + ContextMenuSelectHdl(mxContextMenu->popup_at_rect( + GetDrawingArea(), tools::Rectangle(maPosition, Size(1, 1)))); + Invalidate(); + } + else if (mViewMode == TemplateViewMode::eListView) + ContextMenuSelectHdl(mxContextMenu->popup_at_rect( + mxTreeView.get(), tools::Rectangle(maPosition, Size(1, 1)))); +} + +void TemplateDlgLocalView::ContextMenuSelectHdl(const OString& rIdent) +{ + if (rIdent == "open") + maOpenTemplateHdl.Call(maSelectedItem); + else if (rIdent == "edit") + maEditTemplateHdl.Call(maSelectedItem); + else if (rIdent == "rename") + { + InputDialog aTitleEditDlg(GetDrawingArea(), SfxResId(STR_RENAME_TEMPLATE)); + OUString sOldTitle = maSelectedItem->getTitle(); + aTitleEditDlg.SetEntryText(sOldTitle); + aTitleEditDlg.HideHelpBtn(); + + if (!aTitleEditDlg.run()) + return; + OUString sNewTitle = comphelper::string::strip(aTitleEditDlg.GetEntryText(), ' '); + + if (!sNewTitle.isEmpty() && sNewTitle != sOldTitle) + { + maSelectedItem->setTitle(sNewTitle); + } + ListView::rename(OUString::number(maSelectedItem->mnId), maSelectedItem->maTitle); + } + else if (rIdent == "delete") + { + std::unique_ptr<weld::MessageDialog> xQueryDlg(Application::CreateMessageDialog( + GetDrawingArea(), VclMessageType::Question, VclButtonsType::YesNo, + SfxResId(STR_QMSG_SEL_TEMPLATE_DELETE))); + if (xQueryDlg->run() != RET_YES) + return; + + maDeleteTemplateHdl.Call(maSelectedItem); + reload(); + ListView::remove(OUString::number(maSelectedItem->mnId)); + } + else if (rIdent == "default") + { + maDefaultTemplateHdl.Call(maSelectedItem); + ListView::refreshDefaultColumn(); + } +} + +void TemplateDlgLocalView::insertFilteredItems() +{ + ListView::clearListView(); + for (const ThumbnailViewItem* rItem : mFilteredItemList) + { + const TemplateViewItem* pViewItem = static_cast<const TemplateViewItem*>(rItem); + if (!pViewItem) + return; + bool isDefault = pViewItem->IsDefaultTemplate(); + OUString sId = OUString::number(pViewItem->mnId); + ListView::AppendItem(sId, rItem->maTitle, getRegionName(pViewItem->mnRegionId), + pViewItem->getPath(), isDefault); + } + ListView::sort(); +} + +void TemplateDlgLocalView::setTemplateViewMode(TemplateViewMode eMode) { mViewMode = eMode; } + +void TemplateDlgLocalView::Show() +{ + if (mViewMode == TemplateViewMode::eListView) + { + ThumbnailView::Hide(); + ListView::ShowListView(); + } + else + { + ThumbnailView::Show(); + ListView::HideListView(); + } + syncCursor(); +} +void TemplateDlgLocalView::Hide() +{ + ThumbnailView::Hide(); + ListView::HideListView(); +} + +bool TemplateDlgLocalView::IsVisible() +{ + return ThumbnailView::IsVisible() || ListView::IsListViewVisible(); +} + +void TemplateDlgLocalView::syncCursor() +{ + if (mViewMode == TemplateViewMode::eListView) + { + ListView::unselect_all(); + int nIndex = -1; + + for (auto it = mFilteredItemList.cbegin(); it != mFilteredItemList.cend(); ++it) + { + if ((*it)->mbSelected) + { + nIndex = -1; + nIndex = ListView::get_index((*it)->mnId); + if (nIndex >= 0) + { + ListView::set_cursor(nIndex); + ListView::select(nIndex); + break; + } + } + } + updateSelection(); + } + else + { + ThumbnailView::deselectItems(); + std::vector<int> aSelRows = ListView::get_selected_rows(); + if (aSelRows.empty()) + return; + sal_uInt16 nCursorId = ListView::get_cursor_nId(); + ThumbnailView::SelectItem(nCursorId); + MakeItemVisible(nCursorId); + + for (auto it = mFilteredItemList.begin(); it != mFilteredItemList.end(); ++it) + { + if ((*it)->mnId == nCursorId) + { + mpStartSelRange = it; + break; + } + } + + size_t nPos = GetItemPos(nCursorId); + ThumbnailViewItem* pItem = ImplGetItem(nPos); + const TemplateViewItem* pViewItem = dynamic_cast<const TemplateViewItem*>(pItem); + if (pViewItem) + maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem); + } +} + +void TemplateDlgLocalView::updateSelection() +{ + ThumbnailView::deselectItems(); + for (auto nIndex : ListView::get_selected_rows()) + { + ThumbnailView::SelectItem(ListView::get_nId(nIndex)); + } + + sal_uInt16 nCursorId = ListView::get_cursor_nId(); + size_t nPos = GetItemPos(nCursorId); + ThumbnailViewItem* pItem = ImplGetItem(nPos); + const TemplateViewItem* pViewItem = dynamic_cast<const TemplateViewItem*>(pItem); + if (pViewItem) + maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem); + return; +} + +IMPL_LINK_NOARG(TemplateDlgLocalView, RowActivatedHdl, weld::TreeView&, bool) +{ + maOpenTemplateHdl.Call(maSelectedItem); + return true; +} + +IMPL_LINK(TemplateDlgLocalView, PopupMenuHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + if (rCEvt.IsMouseEvent()) + { + if (ListView::get_selected_rows().empty()) + return true; + int nIndex = ListView::get_cursor_index(); + ListView::unselect_all(); + ListView::select(nIndex); + ListView::set_cursor(nIndex); + Point aPosition(rCEvt.GetMousePosPixel()); + maPosition = aPosition; + updateSelection(); + if (maSelectedItem) + maCreateContextMenuHdl.Call(maSelectedItem); + return true; + } + else + { + if (ListView::get_selected_rows().empty()) + return true; + int nIndex = ListView::get_cursor_index(); + ListView::unselect_all(); + ListView::select(nIndex); + ListView::set_cursor(nIndex); + maPosition = Point(0, 0); + updateSelection(); + if (maSelectedItem) + maCreateContextMenuHdl.Call(maSelectedItem); + return true; + } +} + +IMPL_LINK_NOARG(TemplateDlgLocalView, ListViewChangedHdl, weld::TreeView&, void) +{ + updateSelection(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sfx2/source/control/templatesearchview.cxx b/sfx2/source/control/templatesearchview.cxx index d1f36de241f3..e986d6bde41c 100644 --- a/sfx2/source/control/templatesearchview.cxx +++ b/sfx2/source/control/templatesearchview.cxx @@ -26,11 +26,18 @@ #define MNI_DELETE "delete" TemplateSearchView::TemplateSearchView(std::unique_ptr<weld::ScrolledWindow> xWindow, - std::unique_ptr<weld::Menu> xMenu) + std::unique_ptr<weld::Menu> xMenu, + std::unique_ptr<weld::TreeView> xTreeView) : ThumbnailView(std::move(xWindow), std::move(xMenu)) + , ListView(std::move(xTreeView)) , maSelectedItem(nullptr) , maPosition(0,0) + , mViewMode(TemplateViewMode::eThumbnailView) { + mxTreeView->connect_row_activated(LINK(this, TemplateSearchView, RowActivatedHdl)); + mxTreeView->connect_column_clicked(LINK(this, ListView, ColumnClickedHdl)); + mxTreeView->connect_changed(LINK(this, TemplateSearchView, ListViewChangedHdl)); + mxTreeView->connect_popup_menu(LINK(this, TemplateSearchView, PopupMenuHdl)); } bool TemplateSearchView::MouseButtonDown( const MouseEvent& rMEvt ) @@ -137,10 +144,15 @@ void TemplateSearchView::createContextMenu(const bool bIsDefault) mxContextMenu->append_separator("separator"); mxContextMenu->append(MNI_DELETE,SfxResId(STR_DELETE)); - maSelectedItem->setSelection(true); - maItemStateHdl.Call(maSelectedItem); - ContextMenuSelectHdl(mxContextMenu->popup_at_rect(GetDrawingArea(), tools::Rectangle(maPosition, Size(1,1)))); - Invalidate(); + if(mViewMode == TemplateViewMode::eThumbnailView) + { + maSelectedItem->setSelection(true); + maItemStateHdl.Call(maSelectedItem); + ContextMenuSelectHdl(mxContextMenu->popup_at_rect(GetDrawingArea(), tools::Rectangle(maPosition, Size(1,1)))); + Invalidate(); + } + else if(mViewMode == TemplateViewMode::eListView) + ContextMenuSelectHdl(mxContextMenu->popup_at_rect(mxTreeView.get(), tools::Rectangle(maPosition, Size(1,1)))); } void TemplateSearchView::ContextMenuSelectHdl(const OString& rIdent) @@ -157,12 +169,16 @@ void TemplateSearchView::ContextMenuSelectHdl(const OString& rIdent) return; maDeleteTemplateHdl.Call(maSelectedItem); + ListView::remove(OUString::number(maSelectedItem->mnId)); RemoveItem(maSelectedItem->mnId); CalculateItemPositions(); } else if (rIdent == MNI_DEFAULT_TEMPLATE) + { maDefaultTemplateHdl.Call(maSelectedItem); + ListView::refreshDefaultColumn(); + } } void TemplateSearchView::setCreateContextMenuHdl(const Link<ThumbnailViewItem*,void> &rLink) @@ -217,11 +233,165 @@ void TemplateSearchView::AppendItem(sal_uInt16 nAssocItemId, sal_uInt16 nRegionI if (TemplateLocalView::IsDefaultTemplate(rPath)) pItem->showDefaultIcon(true); + bool isDefault = pItem->IsDefaultTemplate(); + OUString sId = OUString::number(pItem->mnId); + ListView::AppendItem(sId, rTitle, rSubtitle, rPath, isDefault); ThumbnailView::AppendItem(std::move(pItem)); CalculateItemPositions(); } +void TemplateSearchView::Clear() +{ + ThumbnailView::Clear(); + ListView::clearListView(); +} + +void TemplateSearchView::setTemplateViewMode ( TemplateViewMode eMode ) +{ + mViewMode = eMode; +} + +void TemplateSearchView::Show() +{ + if ( mViewMode == TemplateViewMode::eListView) + { + ThumbnailView::Hide(); + ListView::ShowListView(); + } + else + { + ThumbnailView::Show(); + ListView::HideListView(); + } + syncCursor(); +} + +void TemplateSearchView::Hide() +{ + ThumbnailView::Hide(); + ListView::HideListView(); +} + +bool TemplateSearchView::IsVisible() +{ + return ThumbnailView::IsVisible() || ListView::IsListViewVisible(); +} + +void TemplateSearchView::syncCursor() +{ + if ( mViewMode == TemplateViewMode::eListView) + { + ListView::unselect_all(); + int nIndex = -1; + + for(auto it = mFilteredItemList.cbegin(); it != mFilteredItemList.cend() ; ++it ) + { + if((*it)->mbSelected) + { + nIndex = -1; + nIndex = ListView::get_index((*it)->mnId); + if(nIndex >= 0) + { + ListView::set_cursor(nIndex); + ListView::select(nIndex); + break; + } + } + } + updateSelection(); + } + else + { + ThumbnailView::deselectItems(); + std::vector<int> aSelRows = ListView::get_selected_rows(); + if(aSelRows.empty()) + return; + sal_uInt16 nCursorId = ListView::get_cursor_nId(); + ThumbnailView::SelectItem(nCursorId); + MakeItemVisible(nCursorId); + + for(auto it = mFilteredItemList.begin(); it != mFilteredItemList.end() ; ++it ) + { + if((*it)->mnId == nCursorId) + { + mpStartSelRange = it; + break; + } + } + + size_t nPos = GetItemPos(nCursorId); + ThumbnailViewItem* pItem = ImplGetItem(nPos); + const TemplateViewItem *pViewItem = dynamic_cast<const TemplateViewItem*>(pItem); + if(pViewItem) + maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem); + } +} + +void TemplateSearchView::updateSelection() +{ + ThumbnailView::deselectItems(); + for(auto nIndex : ListView::get_selected_rows()) + { + ThumbnailView::SelectItem(ListView::get_nId(nIndex) ); + } + + sal_uInt16 nCursorId = ListView::get_cursor_nId(); + size_t nPos = GetItemPos(nCursorId); + ThumbnailViewItem* pItem = ImplGetItem(nPos); + const TemplateViewItem *pViewItem = dynamic_cast<const TemplateViewItem*>(pItem); + if(pViewItem) + maSelectedItem = dynamic_cast<TemplateViewItem*>(pItem); + return; +} + +IMPL_LINK_NOARG(TemplateSearchView, RowActivatedHdl, weld::TreeView&, bool) +{ + maOpenTemplateHdl.Call(maSelectedItem); + return true; +} + +IMPL_LINK(TemplateSearchView, PopupMenuHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + if (rCEvt.IsMouseEvent()) + { + if(ListView::get_selected_rows().empty()) + return true; + int nIndex = ListView::get_cursor_index(); + ListView::unselect_all(); + ListView::select(nIndex); + ListView::set_cursor(nIndex); + Point aPosition (rCEvt.GetMousePosPixel()); + maPosition = aPosition; + updateSelection(); + if(maSelectedItem) + maCreateContextMenuHdl.Call(maSelectedItem); + return true; + } + else + { + if(ListView::get_selected_rows().empty()) + return true; + int nIndex = ListView::get_cursor_index(); + ListView::unselect_all(); + ListView::select(nIndex) ; + ListView::set_cursor(nIndex) ; + maPosition = Point(0,0); + updateSelection(); + if(maSelectedItem) + maCreateContextMenuHdl.Call(maSelectedItem); + return true; + } +} + +IMPL_LINK_NOARG(TemplateSearchView, ListViewChangedHdl, weld::TreeView&, void) +{ + updateSelection(); +} + BitmapEx TemplateSearchView::getDefaultThumbnail( const OUString& rPath ) { BitmapEx aImg; @@ -254,5 +424,3 @@ void TemplateSearchView::RemoveDefaultTemplateIcon(std::u16string_view rPath) } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ - - diff --git a/sfx2/source/doc/templatedlg.cxx b/sfx2/source/doc/templatedlg.cxx index 313bf591862a..29751ac97d87 100644 --- a/sfx2/source/doc/templatedlg.cxx +++ b/sfx2/source/doc/templatedlg.cxx @@ -24,7 +24,7 @@ #include <sfx2/filedlghelper.hxx> #include <sfx2/objsh.hxx> #include <sfx2/sfxresid.hxx> -#include <sfx2/templatelocalview.hxx> +#include <sfx2/templatedlglocalview.hxx> #include <templatecontaineritem.hxx> #include <templateviewitem.hxx> #include <sfx2/thumbnailviewitem.hxx> @@ -63,6 +63,7 @@ const char TM_SETTING_MANAGER[] = "TemplateManager"; const char TM_SETTING_LASTFOLDER[] = "LastFolder"; const char TM_SETTING_LASTAPPLICATION[] = "LastApplication"; +const char TM_SETTING_VIEWMODE[] = "ViewMode"; #define MNI_ACTION_NEW_FOLDER "new" #define MNI_ACTION_RENAME_FOLDER "rename" @@ -167,12 +168,17 @@ SfxTemplateManagerDlg::SfxTemplateManagerDlg(weld::Window *pParent) , mxCBXHideDlg(m_xBuilder->weld_check_button("hidedialogcb")) , mxActionBar(m_xBuilder->weld_menu_button("action_menu")) , mxSearchView(new TemplateSearchView(m_xBuilder->weld_scrolled_window("scrollsearch", true), - m_xBuilder->weld_menu("contextmenu1"))) - , mxLocalView(new TemplateLocalView(m_xBuilder->weld_scrolled_window("scrolllocal", true), - m_xBuilder->weld_menu("contextmenu2"))) + m_xBuilder->weld_menu("contextmenu1"), + m_xBuilder->weld_tree_view("treesearch_list"))) + , mxLocalView(new TemplateDlgLocalView(m_xBuilder->weld_scrolled_window("scrolllocal", true), + m_xBuilder->weld_menu("contextmenu2"), + m_xBuilder->weld_tree_view("tree_list"))) , mxTemplateDefaultMenu(m_xBuilder->weld_menu("submenu")) , mxSearchViewWeld(new weld::CustomWeld(*m_xBuilder, "search_view", *mxSearchView)) , mxLocalViewWeld(new weld::CustomWeld(*m_xBuilder, "template_view", *mxLocalView)) + , mxListViewButton(m_xBuilder->weld_toggle_button("list_view_btn")) + , mxThumbnailViewButton(m_xBuilder->weld_toggle_button("thumbnail_view_btn")) + , mViewMode(TemplateViewMode::eThumbnailView) { // Create popup menus mxActionBar->insert_item(0, MNI_ACTION_NEW_FOLDER, SfxResId(STR_CATEGORY_NEW), nullptr, nullptr, TRISTATE_INDET); @@ -216,6 +222,8 @@ SfxTemplateManagerDlg::SfxTemplateManagerDlg(weld::Window *pParent) mxExportButton->connect_clicked(LINK(this, SfxTemplateManagerDlg, ExportClickHdl)); mxImportButton->connect_clicked(LINK(this, SfxTemplateManagerDlg, ImportClickHdl)); mxMoreTemplatesButton->connect_clicked(LINK(this, SfxTemplateManagerDlg, LinkClickHdl)); + mxListViewButton->connect_clicked(LINK(this, SfxTemplateManagerDlg, ListViewHdl)); + mxThumbnailViewButton->connect_clicked(LINK(this, SfxTemplateManagerDlg, ThumbnailViewHdl)); mxSearchFilter->connect_changed(LINK(this, SfxTemplateManagerDlg, SearchUpdateHdl)); mxSearchFilter->connect_focus_in(LINK( this, SfxTemplateManagerDlg, GetFocusHdl )); @@ -244,6 +252,10 @@ SfxTemplateManagerDlg::SfxTemplateManagerDlg(weld::Window *pParent) m_aUpdateDataTimer.SetInvokeHandler(LINK(this, SfxTemplateManagerDlg, ImplUpdateDataHdl)); m_aUpdateDataTimer.SetDebugName( "SfxTemplateManagerDlg UpdateDataTimer" ); m_aUpdateDataTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT); + + mxLocalView->connect_focus_rect(LINK(this, SfxTemplateManagerDlg, FocusRectLocalHdl)); + mxSearchView->connect_focus_rect(LINK(this, SfxTemplateManagerDlg, FocusRectSearchHdl)); + bMakeSelItemVisible = false; } SfxTemplateManagerDlg::~SfxTemplateManagerDlg() @@ -289,6 +301,40 @@ void SfxTemplateManagerDlg::setDocumentModel(const uno::Reference<frame::XModel> m_xModel = rModel; } +void SfxTemplateManagerDlg::setTemplateViewMode(TemplateViewMode eViewMode) +{ + mViewMode = eViewMode; + mxLocalView->setTemplateViewMode(eViewMode); + mxSearchView->setTemplateViewMode(eViewMode); + if ( mViewMode == TemplateViewMode::eListView) + { + mxThumbnailViewButton->set_active(false); + mxListViewButton->set_active(true); + } + else + { + mxThumbnailViewButton->set_active(true); + mxListViewButton->set_active(false); + } + + if (! mxSearchFilter->get_text().isEmpty()) + { + mxSearchView->Show(); + mxLocalView->Hide(); + } + else + { + mxSearchView->Hide(); + mxLocalView->Show(); + } +} + +TemplateViewMode SfxTemplateManagerDlg::getTemplateViewMode() +{ + return mViewMode; +} + + FILTER_APPLICATION SfxTemplateManagerDlg::getCurrentApplicationFilter() const { const sal_Int16 nCurAppId = mxCBApp->get_active(); @@ -360,12 +406,14 @@ void SfxTemplateManagerDlg::readSettings () { OUString aLastFolder; SvtViewOptions aViewSettings( EViewType::Dialog, TM_SETTING_MANAGER ); + sal_Int16 nViewMode = -1; if ( aViewSettings.Exists() ) { sal_uInt16 nTmp = 0; aViewSettings.GetUserItem(TM_SETTING_LASTFOLDER) >>= aLastFolder; aViewSettings.GetUserItem(TM_SETTING_LASTAPPLICATION) >>= nTmp; + aViewSettings.GetUserItem(TM_SETTING_VIEWMODE) >>= nViewMode; //open last remembered application only when application model is not set if(!m_xModel.is()) @@ -406,6 +454,18 @@ void SfxTemplateManagerDlg::readSettings () mxLocalView->showRegion(aLastFolder); mxActionBar->set_item_visible(MNI_ACTION_RENAME_FOLDER, true); } + + if(nViewMode == static_cast<sal_Int16>(TemplateViewMode::eListView) || + nViewMode == static_cast<sal_Int16>(TemplateViewMode::eThumbnailView)) + { + TemplateViewMode eViewMode = static_cast<TemplateViewMode>(nViewMode); + setTemplateViewMode(eViewMode); + } + else + { + //Default ViewMode + setTemplateViewMode(TemplateViewMode::eThumbnailView); + } } void SfxTemplateManagerDlg::writeSettings () @@ -419,7 +479,8 @@ void SfxTemplateManagerDlg::writeSettings () Sequence< NamedValue > aSettings { { TM_SETTING_LASTFOLDER, css::uno::makeAny(aLastFolder) }, - { TM_SETTING_LASTAPPLICATION, css::uno::makeAny(sal_uInt16(mxCBApp->get_active())) } + { TM_SETTING_LASTAPPLICATION, css::uno::makeAny(sal_uInt16(mxCBApp->get_active())) }, + { TM_SETTING_VIEWMODE, css::uno::makeAny(static_cast<sal_Int16>(getTemplateViewMode()))} }; // write @@ -498,7 +559,10 @@ void SfxTemplateManagerDlg::DefaultTemplateMenuSelectHdl(const OString& rIdent) } SfxObjectFactory::SetStandardTemplate( aServiceName, OUString() ); - + if (mxSearchView->IsVisible()) + mxSearchView->refreshDefaultColumn(); + else + mxLocalView->refreshDefaultColumn(); createDefaultTemplateMenu(); } @@ -542,6 +606,8 @@ IMPL_LINK_NOARG(SfxTemplateManagerDlg, MoveClickHdl, weld::Button&, void) } mxLocalView->reload(); + if (mxSearchView->IsVisible()) + SearchUpdate(); } IMPL_LINK_NOARG(SfxTemplateManagerDlg, ExportClickHdl, weld::Button&, void) @@ -582,6 +648,8 @@ IMPL_LINK_NOARG(SfxTemplateManagerDlg, ImportClickHdl, weld::Button&, void) mxLocalView->reload(); mxLocalView->showAllTemplates(); + if (mxSearchView->IsVisible()) + SearchUpdate(); mxCBApp->set_active(0); mxCBFolder->set_active(0); mxActionBar->set_item_visible(MNI_ACTION_RENAME_FOLDER, false); @@ -752,6 +820,33 @@ IMPL_LINK_NOARG(SfxTemplateManagerDlg, LoseFocusHdl, weld::Widget&, void) } } +IMPL_LINK_NOARG ( SfxTemplateManagerDlg, ListViewHdl, weld::Button&, void ) +{ + setTemplateViewMode(TemplateViewMode::eListView); +} + +IMPL_LINK_NOARG ( SfxTemplateManagerDlg, ThumbnailViewHdl, weld::Button&, void ) +{ + setTemplateViewMode(TemplateViewMode::eThumbnailView); + bMakeSelItemVisible = true; +} + +IMPL_LINK_NOARG(SfxTemplateManagerDlg, FocusRectLocalHdl, weld::Widget&, tools::Rectangle) +{ + if(bMakeSelItemVisible && !maSelTemplates.empty()) + mxLocalView->MakeItemVisible((*maSelTemplates.begin())->mnId); + bMakeSelItemVisible = false; + return tools::Rectangle(); +} + +IMPL_LINK_NOARG(SfxTemplateManagerDlg, FocusRectSearchHdl, weld::Widget&, tools::Rectangle) +{ + if(bMakeSelItemVisible && !maSelTemplates.empty()) + mxSearchView->MakeItemVisible((*maSelTemplates.begin())->mnId); + bMakeSelItemVisible = false; + return tools::Rectangle(); +} + void SfxTemplateManagerDlg::SearchUpdate() { OUString aKeyword = mxSearchFilter->get_text(); @@ -782,7 +877,7 @@ void SfxTemplateManagerDlg::SearchUpdate() rItem.aPath, rItem.aThumbnail); } - + mxSearchView->sort(); mxSearchView->Invalidate(); } else @@ -792,6 +887,8 @@ void SfxTemplateManagerDlg::SearchUpdate() mxLocalView->Show(); mxLocalView->filterItems(ViewFilter_Application(getCurrentApplicationFilter())); mxLocalView->reload(); + if(mxSearchView->IsVisible()) + SearchUpdate(); OUString sLastFolder = mxCBFolder->get_active_text(); mxLocalView->showRegion(sLastFolder); mxActionBar->set_item_visible(MNI_ACTION_RENAME_FOLDER, true); @@ -1376,6 +1473,7 @@ short SfxTemplateSelectionDlg::run() maIdle.SetPriority(TaskPriority::LOWEST); maIdle.SetInvokeHandler(LINK(this,SfxTemplateSelectionDlg,TimeOut)); maIdle.Start(); + setTemplateViewMode(TemplateViewMode::eThumbnailView); return weld::GenericDialogController::run(); } @@ -1401,4 +1499,4 @@ IMPL_LINK_NOARG(SfxTemplateSelectionDlg, OkClickHdl, weld::Button&, void) m_xDialog->response(RET_OK); } -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file diff --git a/sfx2/source/inc/templatesearchview.hxx b/sfx2/source/inc/templatesearchview.hxx index 24becb6146ba..caf9adca3c75 100644 --- a/sfx2/source/inc/templatesearchview.hxx +++ b/sfx2/source/inc/templatesearchview.hxx @@ -11,17 +11,19 @@ #define INCLUDED_SFX2_SOURCE_INC_TEMPLATESEARCHVIEW_HXX #include <sfx2/thumbnailview.hxx> +#include <sfx2/listview.hxx> class TemplateViewItem; class PopupMenu; class Menu; -class TemplateSearchView final : public ThumbnailView +class TemplateSearchView final : public ThumbnailView, public ListView { public: TemplateSearchView(std::unique_ptr<weld::ScrolledWindow> xWindow, - std::unique_ptr<weld::Menu> xMenu); + std::unique_ptr<weld::Menu> xMenu, + std::unique_ptr<weld::TreeView> xTreeView); void setOpenTemplateHdl (const Link<ThumbnailViewItem*, void> &rLink); @@ -41,6 +43,30 @@ public: const OUString &rTitle, const OUString &rSubtitle, const OUString &rPath, const BitmapEx &rImage ); + void setTemplateViewMode ( TemplateViewMode eMode ); + + void Show() override; + + void Hide() override; + + void Clear() override; + + bool IsVisible(); + + void syncCursor(); + + void updateSelection(); + + void connect_focus_rect(const Link<weld::Widget&, tools::Rectangle>& rLink) { GetDrawingArea()->connect_focus_rect(rLink);} + + void MakeItemVisible( sal_uInt16 nId ) { ThumbnailView::MakeItemVisible(nId);} + + DECL_LINK(RowActivatedHdl, weld::TreeView&, bool); + + DECL_LINK(ListViewChangedHdl, weld::TreeView&, void); + + DECL_LINK(PopupMenuHdl, const CommandEvent&, bool); + static BitmapEx getDefaultThumbnail( const OUString& rPath ); void RemoveDefaultTemplateIcon(std::u16string_view rPath); @@ -63,8 +89,9 @@ private: Link<ThumbnailViewItem*,void> maEditTemplateHdl; Link<ThumbnailViewItem*,void> maDeleteTemplateHdl; Link<ThumbnailViewItem*,void> maDefaultTemplateHdl; + TemplateViewMode mViewMode; }; #endif // INCLUDED_SFX2_SOURCE_INC_TEMPLATESEARCHVIEW_HXX -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file diff --git a/sfx2/uiconfig/ui/templatedlg.ui b/sfx2/uiconfig/ui/templatedlg.ui index 7e0caa03052d..2a352fb268a3 100644 --- a/sfx2/uiconfig/ui/templatedlg.ui +++ b/sfx2/uiconfig/ui/templatedlg.ui @@ -35,6 +35,16 @@ <property name="can-focus">False</property> <property name="icon-name">sfx2/res/actionaction013.png</property> </object> + <object class="GtkImage" id="image8"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">svx/res/galicon.png</property> + </object> + <object class="GtkImage" id="image9"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">svx/res/gallist.png</property> + </object> <object class="GtkMenu" id="menu1"> <property name="visible">True</property> <property name="can-focus">False</property> @@ -53,6 +63,24 @@ </object> </child> </object> + <object class="GtkTreeStore" id="tree_store"> + <columns> + <!-- column-name default_img --> + <column type="GdkPixbuf"/> + <!-- column-name name --> + <column type="gchararray"/> + <!-- column-name category --> + <column type="gchararray"/> + <!-- column-name application --> + <column type="gchararray"/> + <!-- column-name modified --> + <column type="gchararray"/> + <!-- column-name size --> + <column type="gchararray"/> + <!-- column-name id --> + <column type="gchararray"/> + </columns> + </object> <object class="GtkDialog" id="TemplateDialog"> <property name="width-request">740</property> <property name="height-request">500</property> @@ -172,6 +200,50 @@ <property name="can-focus">False</property> <property name="spacing">12</property> <child> + <object class="GtkBox" id="box7"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkToggleButton" id="thumbnail_view_btn"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="tooltip-text" translatable="yes" context="templatedlg|thumbnail_view_btn|tooltip_text">Thumbnail View</property> + <property name="image">image8</property> + <property name="relief">none</property> + <property name="always-show-image">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkToggleButton" id="list_view_btn"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="tooltip-text" translatable="yes" context="templatedlg|list_view_btn|tooltip_text">List View</property> + <property name="image">image9</property> + <property name="relief">none</property> + <property name="always-show-image">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack-type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> <object class="GtkBox" id="box6"> <property name="visible">True</property> <property name="can-focus">False</property> @@ -217,7 +289,7 @@ <property name="expand">False</property> <property name="fill">True</property> <property name="pack-type">end</property> - <property name="position">0</property> + <property name="position">1</property> </packing> </child> <child> @@ -230,7 +302,7 @@ <property name="expand">False</property> <property name="fill">True</property> <property name="pack-type">end</property> - <property name="position">1</property> + <property name="position">2</property> </packing> </child> </object> @@ -326,6 +398,246 @@ <property name="position">1</property> </packing> </child> + <child> + <object class="GtkScrolledWindow"> + <property name="can-focus">True</property> + <property name="no-show-all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="shadow-type">in</property> + <child> + <object class="GtkTreeView" id="tree_list"> + <property name="can-focus">True</property> + <property name="no-show-all">True</property> + <property name="has-tooltip">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">tree_store</property> + <property name="enable-search">False</property> + <property name="search-column">0</property> + <property name="show-expanders">False</property> + <child internal-child="selection"> + <object class="GtkTreeSelection"> + <property name="mode">multiple</property> + </object> + </child> + <child> + <object class="GtkTreeViewColumn"> + <child> + <object class="GtkCellRendererPixbuf" id="cellrenderer1"/> + <attributes> + <attribute name="pixbuf">0</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn1"> + <property name="resizable">True</property> + <property name="spacing">6</property> + <property name="title" translatable="yes" context="templatedlg|treeviewcolumn1">Name</property> + <property name="expand">True</property> + <property name="clickable">True</property> + <child> + <object class="GtkCellRendererText" id="cellrenderer2"> + <property name="ypad">3</property> + </object> + <attributes> + <attribute name="text">1</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn2"> + <property name="resizable">True</property> + <property name="spacing">6</property> + <property name="title" translatable="yes" context="templatedlg|treeviewcolumn2">Category</property> + <property name="clickable">True</property> + <child> + <object class="GtkCellRendererText" id="cellrenderer3"> + <property name="ypad">3</property> + </object> + <attributes> + <attribute name="text">2</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn3"> + <property name="resizable">True</property> + <property name="title" translatable="yes" context="templatedlg|treeviewcolumn3">Application</property> + <property name="clickable">True</property> + <child> + <object class="GtkCellRendererText" id="cellrenderer4"> + <property name="ypad">3</property> + </object> + <attributes> + <attribute name="text">3</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn4"> + <property name="resizable">True</property> + <property name="title" translatable="yes" context="templatedlg|treeviewcolumn4">Modified</property> + <property name="clickable">True</property> + <child> + <object class="GtkCellRendererText" id="cellrenderer5"> + <property name="ypad">3</property> + </object> + <attributes> + <attribute name="text">4</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn5"> + <property name="resizable">True</property> + <property name="title" translatable="yes" context="templatedlg|treeviewcolumn5">Size</property> + <property name="clickable">True</property> + <child> + <object class="GtkCellRendererText" id="cellrenderer6"> + <property name="ypad">3</property> + </object> + <attributes> + <attribute name="text">5</attribute> + </attributes> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow"> + <property name="can-focus">True</property> + <property name="no-show-all">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="shadow-type">in</property> + <child> + <object class="GtkTreeView" id="treesearch_list"> + <property name="can-focus">True</property> + <property name="no-show-all">True</property> + <property name="has-tooltip">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="model">tree_store</property> + <property name="enable-search">False</property> + <property name="search-column">0</property> + <property name="show-expanders">False</property> + <child internal-child="selection"> + <object class="GtkTreeSelection"> + <property name="mode">multiple</property> + </object> + </child> + <child> + <object class="GtkTreeViewColumn"> + <child> + <object class="GtkCellRendererPixbuf" id="cellrenderer7"/> + <attributes> + <attribute name="pixbuf">0</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn6"> + <property name="resizable">True</property> + <property name="spacing">6</property> + <property name="title" translatable="yes" context="templatedlg|treeviewcolumn1">Name</property> + <property name="expand">True</property> + <property name="clickable">True</property> + <child> + <object class="GtkCellRendererText" id="cellrenderer8"> + <property name="ypad">3</property> + </object> + <attributes> + <attribute name="text">1</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn7"> + <property name="resizable">True</property> + <property name="spacing">6</property> + <property name="title" translatable="yes" context="templatedlg|treeviewcolumn2">Category</property> + <property name="clickable">True</property> + <child> + <object class="GtkCellRendererText" id="cellrenderer9"> + <property name="ypad">3</property> + </object> + <attributes> + <attribute name="text">2</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn8"> + <property name="resizable">True</property> + <property name="title" translatable="yes" context="templatedlg|treeviewcolumn3">Application</property> + <property name="clickable">True</property> + <child> + <object class="GtkCellRendererText" id="cellrenderer10"> + <property name="ypad">3</property> + </object> + <attributes> + <attribute name="text">3</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn9"> + <property name="resizable">True</property> + <property name="title" translatable="yes" context="templatedlg|treeviewcolumn4">Modified</property> + <property name="clickable">True</property> + <child> + <object class="GtkCellRendererText" id="cellrenderer11"> + <property name="ypad">3</property> + </object> + <attributes> + <attribute name="text">4</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn10"> + <property name="resizable">True</property> + <property name="title" translatable="yes" context="templatedlg|treeviewcolumn5">Size</property> + <property name="clickable">True</property> + <child> + <object class="GtkCellRendererText" id="cellrenderer12"> + <property name="ypad">3</property> + </object> + <attributes> + <attribute name="text">5</attribute> + </attributes> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> </object> </child> <child type="label"> |