diff options
Diffstat (limited to 'sw/source/ui/misc/bookmark.cxx')
-rw-r--r-- | sw/source/ui/misc/bookmark.cxx | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/sw/source/ui/misc/bookmark.cxx b/sw/source/ui/misc/bookmark.cxx new file mode 100644 index 000000000..a25e85164 --- /dev/null +++ b/sw/source/ui/misc/bookmark.cxx @@ -0,0 +1,516 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <rtl/ustrbuf.hxx> +#include <sfx2/request.hxx> +#include <svl/stritem.hxx> +#include <unotools/viewoptions.hxx> +#include <vcl/weld.hxx> +#include <o3tl/string_view.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/text/XBookmarksSupplier.hpp> +#include <officecfg/Office/Common.hxx> + +#include <swabstdlg.hxx> +#include <swuiexp.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <cmdid.h> +#include <bookmark.hxx> +#include <docsh.hxx> +#include <ndtxt.hxx> +#include <strings.hrc> +#include <IDocumentSettingAccess.hxx> + +using namespace ::com::sun::star; + +const char BookmarkTable::cSeparator(';'); + +// callback to modify EditBox +IMPL_LINK_NOARG(SwInsertBookmarkDlg, ModifyHdl, weld::Entry&, void) +{ + ValidateBookmarks(); + m_xBookmarksBox->unselect_all(); + // if a string has been pasted from the clipboard then + // there may be illegal characters in the box + // sanitization + OUString sTmp = m_xEditBox->get_text(); + OUString sMsg; + const sal_Int32 nLen = sTmp.getLength(); + for (sal_Int32 i = 0; i < BookmarkTable::aForbiddenChars.getLength(); i++) + { + const sal_Int32 nTmpLen = sTmp.getLength(); + sTmp = sTmp.replaceAll(OUStringChar(BookmarkTable::aForbiddenChars.getStr()[i]), ""); + if (sTmp.getLength() != nTmpLen) + sMsg += OUStringChar(BookmarkTable::aForbiddenChars.getStr()[i]); + } + const bool bHasForbiddenChars = sTmp.getLength() != nLen; + m_xForbiddenChars->set_visible(bHasForbiddenChars); + if (bHasForbiddenChars) + m_xEditBox->set_message_type(weld::EntryMessageType::Error); + else + m_xEditBox->set_message_type(weld::EntryMessageType::Normal); + + sal_Int32 nSelectedEntries = 0; + sal_Int32 nEntries = 0; + sal_Int32 nTokenIndex = 0; + while (!sTmp.isEmpty() && nTokenIndex >= 0) + { + OUString aToken = sTmp.getToken(0, BookmarkTable::cSeparator, nTokenIndex); + if (m_xBookmarksBox->GetBookmarkByName(aToken)) + { + m_xBookmarksBox->SelectByName(aToken); + nSelectedEntries++; + } + nEntries++; + } + + // allow to add new bookmark only if one name provided and it's not taken + m_xInsertBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 0 && !bHasForbiddenChars + && !m_bAreProtected); + + // allow to delete only if all bookmarks are recognized + m_xDeleteBtn->set_sensitive(nEntries > 0 && nSelectedEntries == nEntries && !m_bAreProtected); + m_xGotoBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 1); + m_xRenameBtn->set_sensitive(nEntries == 1 && nSelectedEntries == 1 && !m_bAreProtected); +} + +// callback to delete a text mark +IMPL_LINK_NOARG(SwInsertBookmarkDlg, DeleteHdl, weld::Button&, void) +{ + if (!ValidateBookmarks()) + return; + + int nSelectedRows(0); + + m_xBookmarksBox->selected_foreach([this, &nSelectedRows](weld::TreeIter& rEntry) { + // remove from model + sw::mark::IMark* pBookmark + = weld::fromId<sw::mark::IMark*>(m_xBookmarksBox->get_id(rEntry)); + OUString sRemoved = pBookmark->GetName(); + IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); + pMarkAccess->deleteMark(pMarkAccess->findMark(sRemoved), false); + SfxRequest aReq(rSh.GetView().GetViewFrame(), FN_DELETE_BOOKMARK); + aReq.AppendItem(SfxStringItem(FN_DELETE_BOOKMARK, sRemoved)); + aReq.Done(); + aTableBookmarks.erase(std::remove(aTableBookmarks.begin(), aTableBookmarks.end(), + std::make_pair(pBookmark, sRemoved)), + aTableBookmarks.end()); + + ++nSelectedRows; + + return false; + }); + + if (!nSelectedRows) + return; + + // remove from BookmarkTable + m_xBookmarksBox->remove_selection(); + + ValidateBookmarks(); + + m_xDeleteBtn->set_sensitive(false); + m_xGotoBtn->set_sensitive(false); + m_xRenameBtn->set_sensitive(false); + m_xInsertBtn->set_sensitive(false); +} + +// callback to a goto button +IMPL_LINK_NOARG(SwInsertBookmarkDlg, GotoHdl, weld::Button&, void) { GotoSelectedBookmark(); } + +IMPL_LINK_NOARG(SwInsertBookmarkDlg, DoubleClickHdl, weld::TreeView&, bool) +{ + GotoSelectedBookmark(); + return true; +} + +IMPL_LINK_NOARG(SwInsertBookmarkDlg, SelectionChangedHdl, weld::TreeView&, void) +{ + if (!ValidateBookmarks()) + return; + // this event should fired only if we change selection by clicking on BookmarkTable entry + if (!m_xBookmarksBox->has_focus()) + return; + + OUStringBuffer sEditBoxText; + int nSelectedRows = 0; + m_xBookmarksBox->selected_foreach( + [this, &sEditBoxText, &nSelectedRows](weld::TreeIter& rEntry) { + sw::mark::IMark* pBookmark + = weld::fromId<sw::mark::IMark*>(m_xBookmarksBox->get_id(rEntry)); + const OUString& sEntryName = pBookmark->GetName(); + if (!sEditBoxText.isEmpty()) + sEditBoxText.append(";"); + sEditBoxText.append(sEntryName); + ++nSelectedRows; + return false; + }); + if (nSelectedRows) + { + m_xInsertBtn->set_sensitive(false); + m_xGotoBtn->set_sensitive(nSelectedRows == 1); + m_xRenameBtn->set_sensitive(nSelectedRows == 1 && !m_bAreProtected); + m_xDeleteBtn->set_sensitive(!m_bAreProtected); + m_xEditBox->set_text(sEditBoxText.makeStringAndClear()); + } + else + { + m_xInsertBtn->set_sensitive(!m_bAreProtected); + m_xGotoBtn->set_sensitive(false); + m_xRenameBtn->set_sensitive(false); + m_xDeleteBtn->set_sensitive(false); + } +} + +IMPL_LINK_NOARG(SwInsertBookmarkDlg, RenameHdl, weld::Button&, void) +{ + if (!ValidateBookmarks()) + return; + auto xSelected = m_xBookmarksBox->get_selected(); + if (!xSelected) + return; + + sw::mark::IMark* pBookmark + = weld::fromId<sw::mark::IMark*>(m_xBookmarksBox->get_id(*xSelected)); + uno::Reference<frame::XModel> xModel = rSh.GetView().GetDocShell()->GetBaseModel(); + uno::Reference<text::XBookmarksSupplier> xBkms(xModel, uno::UNO_QUERY); + uno::Reference<container::XNameAccess> xNameAccess = xBkms->getBookmarks(); + uno::Any aObj = xNameAccess->getByName(pBookmark->GetName()); + uno::Reference<uno::XInterface> xTmp; + aObj >>= xTmp; + uno::Reference<container::XNamed> xNamed(xTmp, uno::UNO_QUERY); + SwAbstractDialogFactory& rFact = swui::GetFactory(); + ScopedVclPtr<AbstractSwRenameXNamedDlg> pDlg( + rFact.CreateSwRenameXNamedDlg(m_xDialog.get(), xNamed, xNameAccess)); + pDlg->SetForbiddenChars(BookmarkTable::aForbiddenChars + + OUStringChar(BookmarkTable::cSeparator)); + + if (pDlg->Execute()) + { + ValidateBookmarks(); + m_xDeleteBtn->set_sensitive(false); + m_xGotoBtn->set_sensitive(false); + m_xRenameBtn->set_sensitive(false); + m_xInsertBtn->set_sensitive(false); + } +} + +// callback to an insert button. Inserts a new text mark to the current position. +IMPL_LINK_NOARG(SwInsertBookmarkDlg, InsertHdl, weld::Button&, void) +{ + OUString sBookmark = m_xEditBox->get_text(); + rSh.SetBookmark2(vcl::KeyCode(), sBookmark, m_xHideCB->get_active(), + m_xConditionED->get_text()); + + m_xDialog->response(RET_OK); +} + +IMPL_LINK(SwInsertBookmarkDlg, ChangeHideHdl, weld::Toggleable&, rBox, void) +{ + bool bHide = rBox.get_active(); + m_xConditionED->set_sensitive(bHide); + m_xConditionFT->set_sensitive(bHide); +} + +void SwInsertBookmarkDlg::GotoSelectedBookmark() +{ + if (!ValidateBookmarks()) + return; + // if no entries selected we can't jump anywhere + // shouldn't be needed as we disable GoTo button when jump is not possible + auto xSelected = m_xBookmarksBox->get_selected(); + if (!xSelected) + return; + + sw::mark::IMark* pBookmark + = weld::fromId<sw::mark::IMark*>(m_xBookmarksBox->get_id(*xSelected)); + + rSh.EnterStdMode(); + rSh.GotoMark(pBookmark); +} + +bool SwInsertBookmarkDlg::ValidateBookmarks() +{ + if (HaveBookmarksChanged()) + { + PopulateTable(); + m_xEditBox->set_text(""); + return false; + } + return true; +} + +bool SwInsertBookmarkDlg::HaveBookmarksChanged() +{ + IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); + if (pMarkAccess->getBookmarksCount() != m_nLastBookmarksCount) + return true; + + std::vector<std::pair<sw::mark::IMark*, OUString>>::const_iterator aListIter + = aTableBookmarks.begin(); + for (IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); + ppBookmark != pMarkAccess->getBookmarksEnd(); ++ppBookmark) + { + if (IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark)) + { + // more bookmarks then expected + if (aListIter == aTableBookmarks.end()) + return true; + if (aListIter->first != *ppBookmark || aListIter->second != (*ppBookmark)->GetName()) + return true; + ++aListIter; + } + } + // less bookmarks then expected + return aListIter != aTableBookmarks.end(); +} + +void SwInsertBookmarkDlg::PopulateTable() +{ + aTableBookmarks.clear(); + m_xBookmarksBox->clear(); + + IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); + for (IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); + ppBookmark != pMarkAccess->getBookmarksEnd(); ++ppBookmark) + { + if (IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark)) + { + m_xBookmarksBox->InsertBookmark(*ppBookmark); + aTableBookmarks.emplace_back(*ppBookmark, (*ppBookmark)->GetName()); + } + } + m_nLastBookmarksCount = pMarkAccess->getBookmarksCount(); +} + +SwInsertBookmarkDlg::SwInsertBookmarkDlg(weld::Window* pParent, SwWrtShell& rS) + : SfxDialogController(pParent, "modules/swriter/ui/insertbookmark.ui", "InsertBookmarkDialog") + , rSh(rS) + , m_nLastBookmarksCount(0) + , m_bSorted(false) + , m_xEditBox(m_xBuilder->weld_entry("name")) + , m_xInsertBtn(m_xBuilder->weld_button("insert")) + , m_xDeleteBtn(m_xBuilder->weld_button("delete")) + , m_xGotoBtn(m_xBuilder->weld_button("goto")) + , m_xRenameBtn(m_xBuilder->weld_button("rename")) + , m_xHideCB(m_xBuilder->weld_check_button("hide")) + , m_xConditionFT(m_xBuilder->weld_label("condlabel")) + , m_xConditionED(new ConditionEdit(m_xBuilder->weld_entry("withcond"))) + , m_xBookmarksBox(new BookmarkTable(m_xBuilder->weld_tree_view("bookmarks"))) + , m_xForbiddenChars(m_xBuilder->weld_label("lbForbiddenChars")) +{ + m_xBookmarksBox->connect_changed(LINK(this, SwInsertBookmarkDlg, SelectionChangedHdl)); + m_xBookmarksBox->connect_row_activated(LINK(this, SwInsertBookmarkDlg, DoubleClickHdl)); + m_xBookmarksBox->connect_column_clicked(LINK(this, SwInsertBookmarkDlg, HeaderBarClick)); + m_xEditBox->connect_changed(LINK(this, SwInsertBookmarkDlg, ModifyHdl)); + m_xInsertBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, InsertHdl)); + m_xDeleteBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, DeleteHdl)); + m_xGotoBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, GotoHdl)); + m_xRenameBtn->connect_clicked(LINK(this, SwInsertBookmarkDlg, RenameHdl)); + m_xHideCB->connect_toggled(LINK(this, SwInsertBookmarkDlg, ChangeHideHdl)); + + m_xDeleteBtn->set_sensitive(false); + m_xGotoBtn->set_sensitive(false); + m_xRenameBtn->set_sensitive(false); + + PopulateTable(); + + m_xEditBox->set_text(m_xBookmarksBox->GetNameProposal()); + m_xEditBox->set_position(-1); + + m_xForbiddenChars->set_label(SwResId(STR_BOOKMARK_FORBIDDENCHARS) + " " + + BookmarkTable::aForbiddenChars); + m_xForbiddenChars->set_visible(false); + + if (!officecfg::Office::Common::Misc::ExperimentalMode::get()) + { + m_xHideCB->set_visible(false); + m_xConditionFT->set_visible(false); + m_xConditionED->set_visible(false); + } + + m_bAreProtected = rSh.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS); + + // disabled until "Hide" flag is not checked + m_xConditionED->set_sensitive(false); + m_xConditionFT->set_sensitive(false); + + // restore dialog size + SvtViewOptions aDlgOpt(EViewType::Dialog, "BookmarkDialog"); + if (aDlgOpt.Exists()) + m_xDialog->set_window_state(aDlgOpt.GetWindowState().toUtf8()); +} + +SwInsertBookmarkDlg::~SwInsertBookmarkDlg() +{ + // tdf#146261 - Remember size of bookmark dialog + SvtViewOptions aDlgOpt(EViewType::Dialog, "BookmarkDialog"); + OString sWindowState + = m_xDialog->get_window_state(WindowStateMask::Pos | WindowStateMask::Size); + aDlgOpt.SetWindowState(OUString::fromUtf8(sWindowState)); +} + +IMPL_LINK(SwInsertBookmarkDlg, HeaderBarClick, int, nColumn, void) +{ + if (!m_bSorted) + { + m_xBookmarksBox->make_sorted(); + m_bSorted = true; + } + + bool bSortAtoZ = m_xBookmarksBox->get_sort_order(); + + //set new arrow positions in headerbar + if (nColumn == m_xBookmarksBox->get_sort_column()) + { + bSortAtoZ = !bSortAtoZ; + m_xBookmarksBox->set_sort_order(bSortAtoZ); + } + else + { + int nOldSortColumn = m_xBookmarksBox->get_sort_column(); + if (nOldSortColumn != -1) + m_xBookmarksBox->set_sort_indicator(TRISTATE_INDET, nOldSortColumn); + m_xBookmarksBox->set_sort_column(nColumn); + } + + if (nColumn != -1) + { + //sort lists + m_xBookmarksBox->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn); + } +} + +BookmarkTable::BookmarkTable(std::unique_ptr<weld::TreeView> xControl) + : m_xControl(std::move(xControl)) +{ + m_xControl->set_size_request(-1, m_xControl->get_height_rows(8)); + m_xControl->set_column_fixed_widths({ 40, 110, 150, 160 }); + m_xControl->set_selection_mode(SelectionMode::Multiple); +} + +std::unique_ptr<weld::TreeIter> BookmarkTable::get_selected() const +{ + std::unique_ptr<weld::TreeIter> xIter(m_xControl->make_iterator()); + if (!m_xControl->get_selected(xIter.get())) + xIter.reset(); + return xIter; +} + +void BookmarkTable::InsertBookmark(sw::mark::IMark* pMark) +{ + sw::mark::IBookmark* pBookmark = dynamic_cast<sw::mark::IBookmark*>(pMark); + assert(pBookmark); + + OUString sBookmarkNodeText = pBookmark->GetMarkStart().nNode.GetNode().GetTextNode()->GetText(); + sal_Int32 nBookmarkNodeTextPos = pBookmark->GetMarkStart().nContent.GetIndex(); + sal_Int32 nBookmarkTextLen = 0; + bool bPulledAll = false; + bool bPulling = false; + static const sal_Int32 nMaxTextLen = 50; + + if (pBookmark->IsExpanded()) + { + nBookmarkTextLen = pBookmark->GetMarkEnd().nContent.GetIndex() - nBookmarkNodeTextPos; + } + else + { + if (nBookmarkNodeTextPos == sBookmarkNodeText.getLength()) // no text after bookmark + { + nBookmarkNodeTextPos = std::max<sal_Int32>(0, nBookmarkNodeTextPos - nMaxTextLen); + bPulling = true; + if (nBookmarkNodeTextPos == 0) + bPulledAll = true; + } + nBookmarkTextLen = sBookmarkNodeText.getLength() - nBookmarkNodeTextPos; + } + bool bExceedsLength = nBookmarkTextLen > nMaxTextLen; + nBookmarkTextLen = std::min<sal_Int32>(nMaxTextLen, nBookmarkTextLen); + sBookmarkNodeText + = o3tl::trim(sBookmarkNodeText.subView(nBookmarkNodeTextPos, nBookmarkTextLen)); + if (bExceedsLength) + sBookmarkNodeText += "..."; + else if (bPulling && !bPulledAll) + sBookmarkNodeText = "..." + sBookmarkNodeText; + + const OUString& sHideCondition = pBookmark->GetHideCondition(); + OUString sHidden = SwResId(STR_BOOKMARK_NO); + if (pBookmark->IsHidden() || !sHideCondition.isEmpty()) + sHidden = SwResId(STR_BOOKMARK_YES); + OUString sPageNum = OUString::number(SwPaM(pMark->GetMarkStart()).GetPageNum()); + int nRow = m_xControl->n_children(); + m_xControl->append(weld::toId(pMark), sPageNum); + m_xControl->set_text(nRow, pBookmark->GetName(), 1); + m_xControl->set_text(nRow, sBookmarkNodeText, 2); + m_xControl->set_text(nRow, sHidden, 3); + m_xControl->set_text(nRow, sHideCondition, 4); +} + +std::unique_ptr<weld::TreeIter> BookmarkTable::GetRowByBookmarkName(const OUString& sName) +{ + std::unique_ptr<weld::TreeIter> xRet; + m_xControl->all_foreach([this, &sName, &xRet](weld::TreeIter& rEntry) { + sw::mark::IMark* pBookmark = weld::fromId<sw::mark::IMark*>(m_xControl->get_id(rEntry)); + if (pBookmark->GetName() == sName) + { + xRet = m_xControl->make_iterator(&rEntry); + return true; + } + return false; + }); + return xRet; +} + +sw::mark::IMark* BookmarkTable::GetBookmarkByName(const OUString& sName) +{ + auto xEntry = GetRowByBookmarkName(sName); + if (!xEntry) + return nullptr; + + return weld::fromId<sw::mark::IMark*>(m_xControl->get_id(*xEntry)); +} + +void BookmarkTable::SelectByName(const OUString& sName) +{ + auto xEntry = GetRowByBookmarkName(sName); + if (!xEntry) + return; + select(*xEntry); +} + +OUString BookmarkTable::GetNameProposal() const +{ + OUString sDefaultBookmarkName = SwResId(STR_BOOKMARK_DEF_NAME); + sal_Int32 nHighestBookmarkId = 0; + for (int i = 0, nCount = m_xControl->n_children(); i < nCount; ++i) + { + sw::mark::IMark* pBookmark = weld::fromId<sw::mark::IMark*>(m_xControl->get_id(i)); + const OUString& sName = pBookmark->GetName(); + sal_Int32 nIndex = 0; + if (o3tl::getToken(sName, 0, ' ', nIndex) == sDefaultBookmarkName) + { + sal_Int32 nCurrBookmarkId = o3tl::toInt32(o3tl::getToken(sName, 0, ' ', nIndex)); + nHighestBookmarkId = std::max<sal_Int32>(nHighestBookmarkId, nCurrBookmarkId); + } + } + return sDefaultBookmarkName + " " + OUString::number(nHighestBookmarkId + 1); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |