diff options
author | Jakub Trzebiatowski <ubap.dev@gmail.com> | 2016-03-11 20:58:34 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.co.uk> | 2016-03-25 08:53:50 +0000 |
commit | 08da15cabdcef60191f4ed98ed694eba3e35b5e1 (patch) | |
tree | 50ca7ad892cbbfa171f948d831032f5ea4604dcc | |
parent | d1b8dcd5d3fa545df4f4ed2714d4424b32286173 (diff) |
tdf#90855 Improve the 'Insert Bookmark' dialog
implemented:
- display page number
- displaying bookmark text
- goto (button and table doubleclick)
- help
- rename
- selecting multiple bookmarks in TableView or in EditField by ";"
- sorting by any column
Change-Id: I7523dc066380bc360bd484c88a6f4ba45e867320
Reviewed-on: https://gerrit.libreoffice.org/23156
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
-rw-r--r-- | sw/source/ui/misc/bookmark.cxx | 457 | ||||
-rw-r--r-- | sw/source/uibase/inc/bookmark.hxx | 57 | ||||
-rw-r--r-- | sw/uiconfig/swriter/ui/insertbookmark.ui | 188 |
3 files changed, 511 insertions, 191 deletions
diff --git a/sw/source/ui/misc/bookmark.cxx b/sw/source/ui/misc/bookmark.cxx index c12da8dcbdce..a65ddaf545cb 100644 --- a/sw/source/ui/misc/bookmark.cxx +++ b/sw/source/ui/misc/bookmark.cxx @@ -22,220 +22,419 @@ #include <svl/stritem.hxx> #include <vcl/msgbox.hxx> #include <vcl/builderfactory.hxx> +#include <svtools/headbar.hxx> +#include <svtools/treelistentry.hxx> +#include <com/sun/star/text/XBookmarksSupplier.hpp> +#include "swabstdlg.hxx" +#include "swuiexp.hxx" #include "view.hxx" #include "basesh.hxx" #include "wrtsh.hxx" #include "cmdid.h" #include "bookmark.hxx" -#include "IMark.hxx" +#include "docsh.hxx" #include "globals.hrc" -const OUString BookmarkCombo::aForbiddenChars("/\\@*?\";,#"); +using namespace ::com::sun::star; -IMPL_LINK_TYPED( SwInsertBookmarkDlg, ModifyHdl, Edit&, rEdit, void ) +const OUString BookmarkTable::aForbiddenChars("/\\@*?\",#"); +const char BookmarkTable::cSeparator(';'); +const OUString BookmarkTable::sDefaultBookmarkName("Bookmark"); + +// callback to modify EditBox +IMPL_LINK_NOARG_TYPED(SwInsertBookmarkDlg, ModifyHdl, Edit&, void) { - BookmarkCombo& rBox = static_cast<BookmarkCombo&>(rEdit); - bool bSelEntries = rBox.GetSelectEntryCount() != 0; + ValidateBookmarks(); + m_pBookmarksBox->SelectAll(false); // if a string has been pasted from the clipboard then // there may be illegal characters in the box - if(!bSelEntries) + // sanitization + OUString sTmp = m_pEditBox->GetText(); + OUString sMsg; + const sal_Int32 nLen = sTmp.getLength(); + for (sal_Int32 i = 0; i < BookmarkTable::aForbiddenChars.getLength(); i++) { - OUString sTmp = rBox.GetText(); - const sal_Int32 nLen = sTmp.getLength(); - OUString sMsg; - for(sal_Int32 i = 0; i < BookmarkCombo::aForbiddenChars.getLength(); i++) - { - const sal_Int32 nTmpLen = sTmp.getLength(); - sTmp = comphelper::string::remove(sTmp, BookmarkCombo::aForbiddenChars[i]); - if(sTmp.getLength() != nTmpLen) - sMsg += OUString(BookmarkCombo::aForbiddenChars[i]); - } - if(sTmp.getLength() != nLen) + const sal_Int32 nTmpLen = sTmp.getLength(); + sTmp = comphelper::string::remove(sTmp, BookmarkTable::aForbiddenChars[i]); + if (sTmp.getLength() != nTmpLen) + sMsg += OUString(BookmarkTable::aForbiddenChars[i]); + } + if (sTmp.getLength() != nLen) + { + m_pEditBox->SetText(sTmp); + ScopedVclPtr<InfoBox>::Create(this, sRemoveWarning + sMsg)->Execute(); + } + + 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_pBookmarksBox->GetBookmarkByName(aToken)) { - rBox.SetText(sTmp); - ScopedVclPtr<InfoBox>::Create(this, sRemoveWarning+sMsg)->Execute(); + m_pBookmarksBox->SelectByName(aToken); + nSelectedEntries++; } - + nEntries++; } - m_pOkBtn->Enable(!bSelEntries); // new text mark - m_pDeleteBtn->Enable(bSelEntries); // deletable? + // allow to add new bookmark only if one name provided and its not taken + m_pInsertBtn->Enable(nEntries == 1 && nSelectedEntries == 0); + + // allow to delete only if all bookmarks are recognized + m_pDeleteBtn->Enable(nEntries > 0 && nSelectedEntries == nEntries); + m_pGotoBtn->Enable(nEntries == 1 && nSelectedEntries == 1); + m_pRenameBtn->Enable(nEntries == 1 && nSelectedEntries == 1); } // callback to delete a text mark IMPL_LINK_NOARG_TYPED(SwInsertBookmarkDlg, DeleteHdl, Button*, void) { - // remove text marks from the ComboBox + if (!ValidateBookmarks()) + return; + if (m_pBookmarksBox->GetSelectionCount() == 0) + return; - for (sal_Int32 i = m_pBookmarkBox->GetSelectEntryCount(); i; i-- ) - m_pBookmarkBox->RemoveEntryAt(m_pBookmarkBox->GetSelectEntryPos(i - 1)); + SvTreeListEntry* pSelected = m_pBookmarksBox->FirstSelected(); + for (sal_Int32 i = m_pBookmarksBox->GetSelectionCount(); i; i--) + { + // remove from model + sw::mark::IMark* pBookmark = static_cast<sw::mark::IMark*>(pSelected->GetUserData()); + OUString sRemoved = pBookmark->GetName(); + IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); + pMarkAccess->deleteMark(pMarkAccess->findMark(sRemoved)); + 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()); + // remove from BookmarkTable + SvTreeListEntry* nextSelected = m_pBookmarksBox->NextSelected(pSelected); + m_pBookmarksBox->RemoveEntry(pSelected); + pSelected = nextSelected; + } + m_pBookmarksBox->SelectAll(false); + m_pEditBox->SetText(""); + m_pDeleteBtn->Disable(); + m_pGotoBtn->Disable(); + m_pRenameBtn->Disable(); + m_pInsertBtn->Disable(); +} - m_pBookmarkBox->SetText(OUString()); - m_pDeleteBtn->Enable(false); // no further entries there +// callback to a goto button +IMPL_LINK_NOARG_TYPED(SwInsertBookmarkDlg, GotoHdl, Button*, void) +{ + GotoSelectedBookmark(); +} - m_pOkBtn->Enable(); // the OK handler deletes +IMPL_LINK_NOARG_TYPED(SwInsertBookmarkDlg, DoubleClickHdl, SvTreeListBox*, bool) +{ + GotoSelectedBookmark(); + return true; } -// callback for OKButton. Inserts a new text mark to the current position. -// Deleted text marks are also deleted in the model. -void SwInsertBookmarkDlg::Apply() +IMPL_LINK_NOARG_TYPED(SwInsertBookmarkDlg, SelectionChangedHdl, SvTreeListBox*, void) { - //at first remove deleted bookmarks to prevent multiple bookmarks with the same - //name - for (sal_Int32 nCount = m_pBookmarkBox->GetRemovedCount(); nCount > 0; nCount--) + if (!ValidateBookmarks()) + return; + // this event should fired only if we change selection by clicking on BookmarkTable entry + if (!m_pBookmarksBox->HasFocus()) + return; + + OUString sEditBoxText; + SvTreeListEntry* pSelected = m_pBookmarksBox->FirstSelected(); + for (sal_Int32 i = m_pBookmarksBox->GetSelectionCount(); i; i--) { - OUString sRemoved = m_pBookmarkBox->GetRemovedEntry( nCount -1 ).GetName(); - IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); - pMarkAccess->deleteMark( pMarkAccess->findMark(sRemoved) ); - SfxRequest aReq( rSh.GetView().GetViewFrame(), FN_DELETE_BOOKMARK ); - aReq.AppendItem( SfxStringItem( FN_DELETE_BOOKMARK, sRemoved ) ); - aReq.Done(); + sw::mark::IMark* pBookmark = static_cast<sw::mark::IMark*>(pSelected->GetUserData()); + OUString sEntryName = pBookmark->GetName(); + sEditBoxText = sEditBoxText + sEntryName; + if (i > 1) + sEditBoxText += ";"; + pSelected = m_pBookmarksBox->NextSelected(pSelected); } - - // insert text mark - SwBoxEntry aTmpEntry(m_pBookmarkBox->GetText() ); - - if (!m_pBookmarkBox->GetText().isEmpty() && - (m_pBookmarkBox->GetSwEntryPos(aTmpEntry) == COMBOBOX_ENTRY_NOTFOUND)) + if (m_pBookmarksBox->GetSelectionCount() > 0) { - OUString sEntry(comphelper::string::remove(m_pBookmarkBox->GetText(), - m_pBookmarkBox->GetMultiSelectionSeparator())); + m_pInsertBtn->Disable(); + m_pGotoBtn->Enable(m_pBookmarksBox->GetSelectionCount() == 1); + m_pRenameBtn->Enable(m_pBookmarksBox->GetSelectionCount() == 1); + m_pDeleteBtn->Enable(); + m_pEditBox->SetText(sEditBoxText); + } + else + { + m_pInsertBtn->Enable(); + m_pGotoBtn->Disable(); + m_pRenameBtn->Disable(); + m_pDeleteBtn->Disable(); + } +} - rSh.SetBookmark( vcl::KeyCode(), sEntry, OUString() ); - rReq.AppendItem( SfxStringItem( FN_INSERT_BOOKMARK, sEntry ) ); - rReq.Done(); +IMPL_LINK_NOARG_TYPED(SwInsertBookmarkDlg, RenameHdl, Button*, void) +{ + if (!ValidateBookmarks()) + return; + if (m_pBookmarksBox->GetSelectionCount() == 0) + return; + + SvTreeListEntry* pSelected = m_pBookmarksBox->FirstSelected(); + sw::mark::IMark* pBookmark = static_cast<sw::mark::IMark*>(pSelected->GetUserData()); + + 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* pFact = swui::GetFactory(); + OSL_ENSURE(pFact, "SwAbstractDialogFactory fail!"); + std::unique_ptr<AbstractSwRenameXNamedDlg> pDlg(pFact->CreateSwRenameXNamedDlg(this, xNamed, xNameAccess)); + OSL_ENSURE(pDlg, "Dialog creation failed!"); + pDlg->SetForbiddenChars(BookmarkTable::aForbiddenChars + OUStringLiteral1<BookmarkTable::cSeparator>()); + + if (pDlg->Execute()) + { + ValidateBookmarks(); + m_pDeleteBtn->Disable(); + m_pGotoBtn->Disable(); + m_pRenameBtn->Disable(); + m_pInsertBtn->Disable(); } +} - if ( !rReq.IsDone() ) +// callback to a insert button. Inserts a new text mark to the current position. +IMPL_LINK_NOARG_TYPED(SwInsertBookmarkDlg, InsertHdl, Button*, void) +{ + OUString sBookmark = m_pEditBox->GetText(); + rSh.SetBookmark(vcl::KeyCode(), sBookmark, OUString()); + rReq.AppendItem(SfxStringItem(FN_INSERT_BOOKMARK, sBookmark)); + rReq.Done(); + if (!rReq.IsDone()) rReq.Ignore(); + Close(); } -SwInsertBookmarkDlg::SwInsertBookmarkDlg( vcl::Window *pParent, SwWrtShell &rS, SfxRequest& rRequest ) : - SvxStandardDialog(pParent, "InsertBookmarkDialog", "modules/swriter/ui/insertbookmark.ui"), - rSh( rS ), - rReq( rRequest ) +void SwInsertBookmarkDlg::GotoSelectedBookmark() { - get(m_pBookmarkBox, "bookmarks"); - get(m_pOkBtn, "ok"); - get(m_pDeleteBtn, "delete"); + if (!ValidateBookmarks()) + return; + // if no entries selected we cant jump anywhere + // shouldn't be needed as we disable GoTo button when jump is not possible + if (m_pBookmarksBox->GetSelectionCount() == 0) + return; - m_pBookmarkBox->SetModifyHdl(LINK(this, SwInsertBookmarkDlg, ModifyHdl)); - m_pBookmarkBox->EnableMultiSelection(true); - m_pBookmarkBox->EnableAutocomplete( true, true ); + sw::mark::IMark* pBookmark = static_cast<sw::mark::IMark*>(m_pBookmarksBox->FirstSelected()->GetUserData()); - m_pDeleteBtn->SetClickHdl(LINK(this, SwInsertBookmarkDlg, DeleteHdl)); + rSh.EnterStdMode(); + rSh.GotoMark(pBookmark); +} + +bool SwInsertBookmarkDlg::ValidateBookmarks() +{ + if (HaveBookmarksChanged()) + { + PopulateTable(); + m_pEditBox->SetText(""); + return false; + } + return true; +} - // fill Combobox with existing bookmarks +bool SwInsertBookmarkDlg::HaveBookmarksChanged() +{ IDocumentMarkAccess* const pMarkAccess = rSh.getIDocumentMarkAccess(); - for( IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); - ppBookmark != pMarkAccess->getBookmarksEnd(); - ++ppBookmark) + if (pMarkAccess->getBookmarksCount() != static_cast<sal_Int32>(aTableBookmarks.size())) + return true; + + IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin(); + for (sal_Int32 i = 0; i < static_cast<sal_Int32>(aTableBookmarks.size()); i++) { - if(IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark)) + if (IDocumentMarkAccess::MarkType::BOOKMARK == IDocumentMarkAccess::GetType(**ppBookmark)) { - m_pBookmarkBox->InsertSwEntry( - SwBoxEntry(ppBookmark->get()->GetName())); + if (aTableBookmarks[i].first != ppBookmark->get() || + aTableBookmarks[i].second != ppBookmark->get()->GetName()) + return true; } + ++ppBookmark; } - - sRemoveWarning = OUString(SW_RES(STR_REMOVE_WARNING)); + return false; } -SwInsertBookmarkDlg::~SwInsertBookmarkDlg() +void SwInsertBookmarkDlg::PopulateTable() { - disposeOnce(); + aTableBookmarks.clear(); + m_pBookmarksBox->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_pBookmarksBox->InsertBookmark(ppBookmark->get()); + aTableBookmarks.push_back(std::make_pair(ppBookmark->get(), ppBookmark->get()->GetName())); + } + } } -void SwInsertBookmarkDlg::dispose() +void SwInsertBookmarkDlg::Apply() { - m_pBookmarkBox.clear(); - m_pOkBtn.clear(); - m_pDeleteBtn.clear(); - SvxStandardDialog::dispose(); } -BookmarkCombo::BookmarkCombo(vcl::Window* pWin, WinBits nStyle) - : SwComboBox(pWin, nStyle) +SwInsertBookmarkDlg::SwInsertBookmarkDlg(vcl::Window* pParent, SwWrtShell& rS, SfxRequest& rRequest) : + SvxStandardDialog(pParent, "InsertBookmarkDialog", "modules/swriter/ui/insertbookmark.ui"), + rSh(rS), + rReq(rRequest) { + get(m_pBookmarksContainer, "bookmarks"); + get(m_pEditBox, "name"); + get(m_pInsertBtn, "insert"); + get(m_pDeleteBtn, "delete"); + get(m_pGotoBtn, "goto"); + get(m_pRenameBtn, "rename"); + + m_pBookmarksBox = VclPtr<BookmarkTable>::Create(*m_pBookmarksContainer); + + m_pBookmarksBox->SetSelectHdl(LINK(this, SwInsertBookmarkDlg, SelectionChangedHdl)); + m_pBookmarksBox->SetDeselectHdl(LINK(this, SwInsertBookmarkDlg, SelectionChangedHdl)); + m_pBookmarksBox->SetDoubleClickHdl(LINK(this, SwInsertBookmarkDlg, DoubleClickHdl)); + m_pEditBox->SetModifyHdl(LINK(this, SwInsertBookmarkDlg, ModifyHdl)); + m_pInsertBtn->SetClickHdl(LINK(this, SwInsertBookmarkDlg, InsertHdl)); + m_pDeleteBtn->SetClickHdl(LINK(this, SwInsertBookmarkDlg, DeleteHdl)); + m_pGotoBtn->SetClickHdl(LINK(this, SwInsertBookmarkDlg, GotoHdl)); + m_pRenameBtn->SetClickHdl(LINK(this, SwInsertBookmarkDlg, RenameHdl)); + + m_pDeleteBtn->Disable(); + m_pGotoBtn->Disable(); + m_pRenameBtn->Disable(); + + PopulateTable(); + + m_pEditBox->SetText(m_pBookmarksBox->GetNameProposal()); + m_pEditBox->SetCursorAtLast(); + + sRemoveWarning = OUString(SW_RES(STR_REMOVE_WARNING)); } -sal_Int32 BookmarkCombo::GetFirstSelEntryPos() const +SwInsertBookmarkDlg::~SwInsertBookmarkDlg() { - return GetSelEntryPos(0); + disposeOnce(); } -sal_Int32 BookmarkCombo::GetNextSelEntryPos(sal_Int32 nPos) const +void SwInsertBookmarkDlg::dispose() { - return GetSelEntryPos(nPos + 1); + m_pBookmarksBox.disposeAndClear(); + m_pBookmarksContainer.clear(); + m_pInsertBtn.clear(); + m_pDeleteBtn.clear(); + m_pGotoBtn.clear(); + m_pEditBox.clear(); + SvxStandardDialog::dispose(); } -sal_Int32 BookmarkCombo::GetSelEntryPos(sal_Int32 nPos) const +BookmarkTable::BookmarkTable(SvSimpleTableContainer& rParent) : + SvSimpleTable(rParent, 0) { - sal_Unicode cSep = GetMultiSelectionSeparator(); + static long nTabs[] = {3, 0, 30, 150}; - sal_Int32 nCnt = comphelper::string::getTokenCount(GetText(), cSep); - - for (; nPos < nCnt; nPos++) - { - OUString sEntry(comphelper::string::strip(GetText().getToken(nPos, cSep), ' ')); - if (GetEntryPos(sEntry) != COMBOBOX_ENTRY_NOTFOUND) - return nPos; - } + SetTabs(nTabs, MAP_PIXEL); + SetSelectionMode(MULTIPLE_SELECTION); + InsertHeaderEntry("Page"); + InsertHeaderEntry("Name"); + InsertHeaderEntry("Text"); - return COMBOBOX_ENTRY_NOTFOUND; + rParent.SetTable(this); } -sal_Int32 BookmarkCombo::GetSelectEntryCount() const +void BookmarkTable::InsertBookmark(sw::mark::IMark* pMark) { - sal_Int32 nCnt = 0; - - sal_Int32 nPos = GetFirstSelEntryPos(); - while (nPos != COMBOBOX_ENTRY_NOTFOUND) + OUString sBookmarkNodeText = pMark->GetMarkStart().nNode.GetNode().GetTextNode()->GetText(); + sal_Int32 nBookmarkNodeTextPos = pMark->GetMarkStart().nContent.GetIndex(); + sal_Int32 nBookmarkTextLen = 0; + bool bPulledAll = false; + bool bPulling = false; + static const sal_Int32 nMaxTextLen = 50; + + if (pMark->IsExpanded()) { - nPos = GetNextSelEntryPos(nPos); - nCnt++; + nBookmarkTextLen = pMark->GetMarkEnd().nContent.GetIndex() - nBookmarkNodeTextPos; } - - return nCnt; + 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 = sBookmarkNodeText.copy(nBookmarkNodeTextPos, nBookmarkTextLen).trim(); + if (bExceedsLength) + sBookmarkNodeText += "..."; + else if (bPulling && !bPulledAll) + sBookmarkNodeText = "..." + sBookmarkNodeText; + + OUString sPageNum = OUString::number(SwPaM(pMark->GetMarkStart()).GetPageNum()); + OUString sColumnData = sPageNum + "\t" + pMark->GetName() + "\t" + sBookmarkNodeText; + InsertEntryToColumn(sColumnData, TREELIST_APPEND, 0xffff, pMark); } -// position inside of the listbox (the ComboBox) -sal_Int32 BookmarkCombo::GetSelectEntryPos( sal_Int32 nSelIndex ) const +SvTreeListEntry* BookmarkTable::GetRowByBookmarkName(const OUString& sName) { - sal_Int32 nCnt = 0; - sal_Int32 nPos = GetFirstSelEntryPos(); - while (nPos != COMBOBOX_ENTRY_NOTFOUND) + SvTreeListEntry* pEntry = First(); + for (sal_Int32 i = GetRowCount(); i; i--) { - if (nSelIndex == nCnt) + sw::mark::IMark* pBookmark = static_cast<sw::mark::IMark*>(pEntry->GetUserData()); + if (pBookmark->GetName() == sName) { - sal_Unicode cSep = GetMultiSelectionSeparator(); - OUString sEntry(comphelper::string::strip(GetText().getToken(nPos, cSep), ' ')); - return GetEntryPos(sEntry); + return pEntry; } - nPos = GetNextSelEntryPos(nPos); - nCnt++; + pEntry = Next(pEntry); } + return nullptr; +} + +sw::mark::IMark* BookmarkTable::GetBookmarkByName(const OUString& sName) +{ + SvTreeListEntry* pEntry = GetRowByBookmarkName(sName); + if (!pEntry) + return nullptr; - return COMBOBOX_ENTRY_NOTFOUND; + return static_cast<sw::mark::IMark*>(pEntry->GetUserData()); } -bool BookmarkCombo::PreNotify( NotifyEvent& rNEvt ) +void BookmarkTable::SelectByName(const OUString& sName) { - bool bHandled = false; - if( MouseNotifyEvent::KEYINPUT == rNEvt.GetType() && - rNEvt.GetKeyEvent()->GetCharCode() ) + SvTreeListEntry* pEntry = GetRowByBookmarkName(sName); + if (!pEntry) + return; + + Select(pEntry); +} + +OUString BookmarkTable::GetNameProposal() +{ + sal_Int32 nHighestBookmarkId = 0; + SvTreeListEntry* pEntry = First(); + for (sal_Int32 i = GetRowCount(); i; i--) { - OUString sKey( rNEvt.GetKeyEvent()->GetCharCode() ); - if(-1 != aForbiddenChars.indexOf(sKey)) - bHandled = true; + sw::mark::IMark* pBookmark = static_cast<sw::mark::IMark*>(pEntry->GetUserData()); + OUString sName = pBookmark->GetName(); + sal_Int32 nIndex = 0; + if (sName.getToken(0, ' ', nIndex) == sDefaultBookmarkName) + { + sal_Int32 nCurrBookmarkId = sName.getToken(0, ' ', nIndex).toInt32(); + nHighestBookmarkId = std::max<sal_Int32>(nHighestBookmarkId, nCurrBookmarkId); + } + pEntry = Next(pEntry); } - if(!bHandled) - bHandled = SwComboBox::PreNotify( rNEvt ); - return bHandled; + return sDefaultBookmarkName + " " + OUString::number(nHighestBookmarkId + 1); } -VCL_BUILDER_FACTORY_ARGS(BookmarkCombo, 0) - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/inc/bookmark.hxx b/sw/source/uibase/inc/bookmark.hxx index 7a94dc3473eb..0754f6988bac 100644 --- a/sw/source/uibase/inc/bookmark.hxx +++ b/sw/source/uibase/inc/bookmark.hxx @@ -21,47 +21,68 @@ #include <svx/stddlg.hxx> #include <vcl/fixed.hxx> +#include <ndtxt.hxx> #include <vcl/button.hxx> +#include <svtools/simptabl.hxx> +#include <pam.hxx> #include "swlbox.hxx" +#include "IMark.hxx" class SwWrtShell; class SfxRequest; -class BookmarkCombo : public SwComboBox +class BookmarkTable : public SvSimpleTable { - sal_Int32 GetFirstSelEntryPos() const; - sal_Int32 GetNextSelEntryPos(sal_Int32 nPos) const; - sal_Int32 GetSelEntryPos(sal_Int32 nPos) const; - - virtual bool PreNotify(NotifyEvent& rNEvt) override; + SvTreeListEntry* GetRowByBookmarkName(const OUString& sName); public: - BookmarkCombo(vcl::Window* pWin, WinBits nStyle); - - sal_Int32 GetSelectEntryCount() const; - sal_Int32 GetSelectEntryPos( sal_Int32 nSelIndex = 0 ) const; + BookmarkTable(SvSimpleTableContainer& rParent); + void InsertBookmark(sw::mark::IMark* pMark); + void SelectByName(const OUString& sName); + sw::mark::IMark* GetBookmarkByName(const OUString& sName); + OUString GetNameProposal(); static const OUString aForbiddenChars; + static const OUString sDefaultBookmarkName; + static const char cSeparator; }; class SwInsertBookmarkDlg: public SvxStandardDialog { - VclPtr<BookmarkCombo> m_pBookmarkBox; - VclPtr<OKButton> m_pOkBtn; - VclPtr<PushButton> m_pDeleteBtn; - - OUString sRemoveWarning; - SwWrtShell &rSh; - SfxRequest& rReq; + VclPtr<SvSimpleTableContainer> m_pBookmarksContainer; + VclPtr<BookmarkTable> m_pBookmarksBox; + VclPtr<Edit> m_pEditBox; + VclPtr<PushButton> m_pInsertBtn; + VclPtr<PushButton> m_pDeleteBtn; + VclPtr<PushButton> m_pGotoBtn; + VclPtr<PushButton> m_pRenameBtn; + OUString sRemoveWarning; + SwWrtShell& rSh; + SfxRequest& rReq; + std::vector<std::pair<sw::mark::IMark*, OUString>> aTableBookmarks; DECL_LINK_TYPED(ModifyHdl, Edit&, void); + DECL_LINK_TYPED(InsertHdl, Button*, void); DECL_LINK_TYPED(DeleteHdl, Button*, void); + DECL_LINK_TYPED(RenameHdl, Button*, void); + DECL_LINK_TYPED(GotoHdl, Button*, void); + DECL_LINK_TYPED(SelectionChangedHdl, SvTreeListBox*, void); + DECL_LINK_TYPED(DoubleClickHdl, SvTreeListBox*, bool); + // Fill table with bookmarks + void PopulateTable(); + /** + * Check if displayed bookmarks are up-to date, if not update them. + * @return True if no update was needed. + */ + bool ValidateBookmarks(); + bool HaveBookmarksChanged(); + void GotoSelectedBookmark(); virtual void Apply() override; public: - SwInsertBookmarkDlg(vcl::Window *pParent, SwWrtShell &rSh, SfxRequest& rReq); + SwInsertBookmarkDlg(vcl::Window* pParent, SwWrtShell& rSh, SfxRequest& rReq); virtual ~SwInsertBookmarkDlg(); virtual void dispose() override; }; diff --git a/sw/uiconfig/swriter/ui/insertbookmark.ui b/sw/uiconfig/swriter/ui/insertbookmark.ui index 080af59d5f3a..49cd3eb5322e 100644 --- a/sw/uiconfig/swriter/ui/insertbookmark.ui +++ b/sw/uiconfig/swriter/ui/insertbookmark.ui @@ -1,78 +1,76 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.16.0 --> +<!-- Generated with glade 3.18.3 --> <interface> <requires lib="gtk+" version="3.0"/> <object class="GtkDialog" id="InsertBookmarkDialog"> <property name="can_focus">False</property> <property name="border_width">6</property> - <property name="title" translatable="yes">Insert Bookmark</property> + <property name="title" translatable="yes">Bookmark</property> <property name="type_hint">dialog</property> <child internal-child="vbox"> <object class="GtkBox" id="dialog-vbox1"> <property name="can_focus">False</property> <property name="orientation">vertical</property> <property name="spacing">12</property> - <child internal-child="action_area"> - <object class="GtkButtonBox" id="dialog-action_area1"> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> <property name="can_focus">False</property> - <property name="layout_style">end</property> + <property name="spacing">6</property> <child> - <object class="GtkButton" id="ok"> - <property name="label">gtk-ok</property> + <object class="GtkEntry" id="name"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> + <property name="max_width_chars">0</property> </object> <packing> - <property name="expand">False</property> + <property name="expand">True</property> <property name="fill">True</property> + <property name="pack_type">start</property> <property name="position">0</property> </packing> </child> <child> - <object class="GtkButton" id="cancel"> - <property name="label">gtk-cancel</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="delete"> - <property name="label" translatable="yes">Delete</property> + <object class="GtkButtonBox" id="box5"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> + <property name="can_focus">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkButton" id="insert"> + <property name="label">Insert</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> </object> <packing> <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> + <property name="fill">False</property> + <property name="pack_type">end</property> + <property name="position">0</property> </packing> </child> </object> <packing> <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> + <property name="fill">False</property> <property name="position">0</property> </packing> </child> <child> - <object class="swuilo-BookmarkCombo" id="bookmarks"> - <property name="width_request">150</property> - <property name="height_request">200</property> - <property name="visible">True</property> - <property name="can_focus">True</property> + <object class="svtlo-SvSimpleTableContainer" id="bookmarks"> + <property name="width_request">350</property> + <property name="height_request">250</property> + <property name="visible">True</property> + <property name="can_focus">True</property> </object> <packing> <property name="expand">True</property> @@ -80,12 +78,114 @@ <property name="position">1</property> </packing> </child> + <child> + <object class="GtkBox" id="box2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkButtonBox" id="box4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkButton" id="help"> + <property name="label">gtk-help</property> + <property name="width_request">75</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="pack_type">start</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButtonBox" id="box4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="homogeneous">True</property> + <property name="spacing">6</property> + <child> + <object class="GtkButton" id="rename"> + <property name="label" translatable="yes">Rename</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="delete"> + <property name="label" translatable="yes">Delete</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="goto"> + <property name="label" translatable="yes">Go to</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton" id="close"> + <property name="label">gtk-close</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="pack_type">end</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> </object> </child> - <action-widgets> - <action-widget response="0">ok</action-widget> - <action-widget response="0">cancel</action-widget> - <action-widget response="0">delete</action-widget> - </action-widgets> </object> </interface> |