office-gobmx/sfx2/source/dialog/versdlg.cxx
Armin Le Grand (allotropia) 789a737ac9 Remove DeleteItemOnIdlex
There are some CrashReports in 7.6 which have
DeleteItemOnIdle on the stack, but there is nothing
reproducable. So I took a look...

I first thought it's a MCGR regression, due to classes
on the stack. But the Item involved is just random, can
happen with any Item.

Then I thought it may have to do with ITEM refactorings,
but it happens with DeleteItemOnIdle involved, so also
not the case. I already saw DeleteItemOnIdle when doing
these and qualified as 'hack' in the way. already

It is only on Windows and DeleteItemOnIdle is involved.
This again (took a deeper look now) is an old hack to
keep an SfxPoolItem 'alive' for some 'time'. For that,
it triggers an async reschedule which then deletes the
Item when being called. If the Item will be used after
that is pure coincidence - seems to work in most cases.

It seems as if for Windows the timing slightly changed
for some scenarios, so a reschedule is too early. This
can happen with this hack anytime.

DeleteItemOnIdle is used in scenarios where SfxPoolItem*
is e.g. returned, but is *not* anchored, so e.g. not
member of an SfxItemSet. Or in short: Lifetime is not
safe.

DeleteItemOnIdle exists since 1st import, but was
changed to AsyncEvent ca. 4 months ago (see
57145acf9e), so that may
have caused it. It is possible that these errors happen
on Windows since then. Before something more complicated
was used to delete it late, but surely also not really
safe.

Due to ITEM refactor I have the knowledge/tooling to
solve this. It will not be a 1-5 lines fix, but it is
a hack and in the way for further ITEM refactor anyways.
What we have nowadays is a SfxPoolItemHolder -> it's
like an SfxItemSet for a single Item. It safely holds/
controls the lifetime of an SfxPoolItem. It is already
used in quite some places. It helps to solve many hacks,
also the ones putting Items directly to the Pool - due
to there never was an alternative for that. In principle
the ItemPool/ItemSet/Item paradigm was never complete
without SfxPoolItemHolder.

Thus I started to fix that (and remove that hack for
good, sooo many changes over the years, sigh), but as
said is not straightforward. Will have to change
retvals of involved stuff to SfxPoolItemHolder - it's
just two pointers and designed to be copied (one is a
Pool, needed to cleanup when destructing).
CopyConstruct/destroy just counts the RefCnt up/down,
so cheap.

1st version compiling, let's check on gerrit...
Corrected one error in QueryState for securitypage, also
added some security features/asserts.

Change-Id: Ida49fd35ca88ead84b11d93e18b978cb9e395090
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161083
Tested-by: Jenkins
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
2023-12-21 21:13:55 +01:00

469 lines
17 KiB
C++

/* -*- 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 <sal/config.h>
#include <com/sun/star/document/XCmisDocument.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/util/RevisionTag.hpp>
#include <com/sun/star/beans/NamedValue.hpp>
#include <officecfg/Office/Common.hxx>
#include <unotools/localedatawrapper.hxx>
#include <svl/intitem.hxx>
#include <svl/stritem.hxx>
#include <svl/itemset.hxx>
#include <unotools/useroptions.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <tools/datetime.hxx>
#include <versdlg.hxx>
#include <sfx2/strings.hrc>
#include <sfx2/dialoghelper.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/sfxresid.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/sfxsids.hrc>
#include <sfx2/dispatch.hxx>
#include <sfx2/sfxuno.hxx>
#include <memory>
#include <vector>
using namespace com::sun::star;
struct SfxVersionInfo
{
OUString aName;
OUString aComment;
OUString aAuthor;
DateTime aCreationDate;
SfxVersionInfo();
};
class SfxVersionTableDtor
{
private:
std::vector<std::unique_ptr<SfxVersionInfo>> aTableList;
public:
explicit SfxVersionTableDtor( const uno::Sequence < util::RevisionTag >& rInfo );
explicit SfxVersionTableDtor( const uno::Sequence < document::CmisVersion > & rInfo );
SfxVersionTableDtor(const SfxVersionTableDtor&) = delete;
SfxVersionTableDtor& operator=(const SfxVersionTableDtor&) = delete;
size_t size() const
{ return aTableList.size(); }
SfxVersionInfo* at( size_t i ) const
{ return aTableList[ i ].get(); }
};
SfxVersionTableDtor::SfxVersionTableDtor( const uno::Sequence < util::RevisionTag >& rInfo )
{
for ( const auto& rItem : rInfo )
{
std::unique_ptr<SfxVersionInfo> pInfo(new SfxVersionInfo);
pInfo->aName = rItem.Identifier;
pInfo->aComment = rItem.Comment;
pInfo->aAuthor = rItem.Author;
pInfo->aCreationDate = DateTime( rItem.TimeStamp );
aTableList.push_back( std::move(pInfo) );
}
}
SfxVersionTableDtor::SfxVersionTableDtor( const uno::Sequence < document::CmisVersion >& rInfo )
{
for ( const auto& rItem : rInfo )
{
std::unique_ptr<SfxVersionInfo> pInfo(new SfxVersionInfo);
pInfo->aName = rItem.Id;
pInfo->aComment = rItem.Comment;
pInfo->aAuthor = rItem.Author;
pInfo->aCreationDate = DateTime( rItem.TimeStamp );
aTableList.push_back( std::move(pInfo) );
}
}
SfxVersionInfo::SfxVersionInfo()
: aCreationDate( DateTime::EMPTY )
{
}
namespace
{
void setColSizes(weld::TreeView& rVersionBox)
{
// recalculate the datetime column width
int nWidestTime(rVersionBox.get_pixel_size(getWidestDateTime(Application::GetSettings().GetLocaleDataWrapper(), false)).Width());
int nW1 = rVersionBox.get_pixel_size(rVersionBox.get_column_title(1)).Width();
int nMax = std::max(nWidestTime, nW1) + 12; // max width + a little offset
const int nRest = rVersionBox.get_preferred_size().Width() - nMax;
std::set<OUString> aAuthors;
aAuthors.insert(SvtUserOptions().GetFullName());
for (int i = 0; i < rVersionBox.n_children(); ++i)
{
aAuthors.insert(weld::fromId<SfxVersionInfo*>(rVersionBox.get_id(i))->aAuthor);
}
int nMaxAuthorWidth = nRest/4;
for (auto const& author : aAuthors)
{
nMaxAuthorWidth = std::max<int>(nMaxAuthorWidth, rVersionBox.get_pixel_size(author).Width());
if (nMaxAuthorWidth > nRest/2)
{
nMaxAuthorWidth = nRest/2;
break;
}
}
rVersionBox.set_column_fixed_widths({ nMax, nMaxAuthorWidth });
}
}
SfxVersionDialog::SfxVersionDialog(weld::Window* pParent, SfxViewFrame* pVwFrame, bool bIsSaveVersionOnClose)
: SfxDialogController(pParent, "sfx/ui/versionsofdialog.ui", "VersionsOfDialog")
, m_pViewFrame(pVwFrame)
, m_bIsSaveVersionOnClose(bIsSaveVersionOnClose)
, m_xSaveButton(m_xBuilder->weld_button("save"))
, m_xSaveCheckBox(m_xBuilder->weld_check_button("always"))
, m_xOpenButton(m_xBuilder->weld_button("open"))
, m_xViewButton(m_xBuilder->weld_button("show"))
, m_xDeleteButton(m_xBuilder->weld_button("delete"))
, m_xCompareButton(m_xBuilder->weld_button("compare"))
, m_xCmisButton(m_xBuilder->weld_button("cmis"))
, m_xVersionBox(m_xBuilder->weld_tree_view("versions"))
{
m_xVersionBox->set_size_request(m_xVersionBox->get_approximate_digit_width() * 90,
m_xVersionBox->get_height_rows(15));
setColSizes(*m_xVersionBox);
Link<weld::Button&,void> aClickLink = LINK( this, SfxVersionDialog, ButtonHdl_Impl );
m_xViewButton->connect_clicked( aClickLink );
m_xSaveButton->connect_clicked( aClickLink );
m_xDeleteButton->connect_clicked( aClickLink );
m_xCompareButton->connect_clicked( aClickLink );
m_xOpenButton->connect_clicked( aClickLink );
m_xSaveCheckBox->connect_toggled(LINK(this, SfxVersionDialog, ToggleHdl_Impl));
m_xCmisButton->connect_clicked( aClickLink );
m_xVersionBox->connect_changed( LINK( this, SfxVersionDialog, SelectHdl_Impl ) );
m_xVersionBox->connect_row_activated( LINK( this, SfxVersionDialog, DClickHdl_Impl ) );
m_xVersionBox->grab_focus();
// set dialog title (filename or docinfo title)
OUString sText = m_xDialog->get_title() +
" " + m_pViewFrame->GetObjectShell()->GetTitle();
m_xDialog->set_title(sText);
Init_Impl();
}
static OUString ConvertWhiteSpaces_Impl( const OUString& rText )
{
// converted linebreaks and tabs to blanks; it's necessary for the display
OUStringBuffer sConverted;
const sal_Unicode* pChars = rText.getStr();
while ( *pChars )
{
switch ( *pChars )
{
case '\n' :
case '\t' :
sConverted.append(' ');
break;
default:
sConverted.append(*pChars);
}
++pChars;
}
return sConverted.makeStringAndClear();
}
void SfxVersionDialog::Init_Impl()
{
SfxObjectShell *pObjShell = m_pViewFrame->GetObjectShell();
SfxMedium* pMedium = pObjShell->GetMedium();
uno::Sequence < util::RevisionTag > aVersions = pMedium->GetVersionList( true );
m_pTable.reset(new SfxVersionTableDtor( aVersions ));
m_xVersionBox->freeze();
for (size_t n = 0; n < m_pTable->size(); ++n)
{
SfxVersionInfo *pInfo = m_pTable->at( n );
OUString aEntry = formatDateTime(pInfo->aCreationDate, Application::GetSettings().GetLocaleDataWrapper(), false);
m_xVersionBox->append(weld::toId(pInfo), aEntry);
auto nLastRow = m_xVersionBox->n_children() - 1;
m_xVersionBox->set_text(nLastRow, pInfo->aAuthor, 1);
m_xVersionBox->set_text(nLastRow, ConvertWhiteSpaces_Impl(pInfo->aComment), 2);
}
m_xVersionBox->thaw();
if (auto nCount = m_pTable->size())
m_xVersionBox->select(nCount - 1);
m_xSaveCheckBox->set_active(m_bIsSaveVersionOnClose);
bool bEnable = !pObjShell->IsReadOnly();
m_xSaveButton->set_sensitive( bEnable );
m_xSaveCheckBox->set_sensitive( bEnable );
m_xOpenButton->set_sensitive(false);
m_xViewButton->set_sensitive(false);
m_xDeleteButton->set_sensitive(false);
m_xCompareButton->set_sensitive(false);
if ( !officecfg::Office::Common::Misc::ExperimentalMode::get() )
m_xCmisButton->hide( );
uno::Reference<document::XCmisDocument> xCmisDoc(pObjShell->GetModel(), uno::UNO_QUERY);
if (xCmisDoc && xCmisDoc->isVersionable())
m_xCmisButton->set_sensitive(true);
else
m_xCmisButton->set_sensitive(false);
SelectHdl_Impl(*m_xVersionBox);
}
SfxVersionDialog::~SfxVersionDialog()
{
}
void SfxVersionDialog::Open_Impl()
{
SfxObjectShell *pObjShell = m_pViewFrame->GetObjectShell();
auto nPos = m_xVersionBox->get_selected_index();
SfxInt16Item aItem( SID_VERSION, nPos + 1);
SfxStringItem aTarget( SID_TARGETNAME, "_blank" );
SfxStringItem aReferer( SID_REFERER, "private:user" );
SfxStringItem aFile( SID_FILE_NAME, pObjShell->GetMedium()->GetName() );
uno::Sequence< beans::NamedValue > aEncryptionData;
if ( GetEncryptionData_Impl( &pObjShell->GetMedium()->GetItemSet(), aEncryptionData ) )
{
// there is a password, it should be used during the opening
SfxUnoAnyItem aEncryptionDataItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) );
m_pViewFrame->GetDispatcher()->ExecuteList(
SID_OPENDOC, SfxCallMode::ASYNCHRON,
{ &aFile, &aItem, &aTarget, &aReferer, &aEncryptionDataItem });
}
else
{
m_pViewFrame->GetDispatcher()->ExecuteList(
SID_OPENDOC, SfxCallMode::ASYNCHRON,
{ &aFile, &aItem, &aTarget, &aReferer });
}
m_xDialog->response(RET_OK);
}
IMPL_LINK_NOARG(SfxVersionDialog, DClickHdl_Impl, weld::TreeView&, bool)
{
Open_Impl();
return true;
}
IMPL_LINK_NOARG(SfxVersionDialog, SelectHdl_Impl, weld::TreeView&, void)
{
bool bEnable = m_xVersionBox->get_selected_index() != -1;
SfxObjectShell* pObjShell = m_pViewFrame->GetObjectShell();
m_xDeleteButton->set_sensitive(bEnable && !pObjShell->IsReadOnly());
m_xOpenButton->set_sensitive(bEnable);
m_xViewButton->set_sensitive(bEnable);
SfxPoolItemHolder aResult;
m_pViewFrame->GetDispatcher()->QueryState(SID_DOCUMENT_MERGE, aResult);
SfxItemState eState = m_pViewFrame->GetDispatcher()->QueryState(SID_DOCUMENT_COMPARE, aResult);
m_xCompareButton->set_sensitive(bEnable && eState >= SfxItemState::DEFAULT);
}
IMPL_LINK(SfxVersionDialog, ButtonHdl_Impl, weld::Button&, rButton, void)
{
SfxObjectShell *pObjShell = m_pViewFrame->GetObjectShell();
int nEntry = m_xVersionBox->get_selected_index();
if (&rButton == m_xSaveButton.get())
{
SfxVersionInfo aInfo;
aInfo.aAuthor = SvtUserOptions().GetFullName();
SfxViewVersionDialog_Impl aDlg(m_xDialog.get(), aInfo, true);
short nRet = aDlg.run();
if (nRet == RET_OK)
{
SfxStringItem aComment( SID_DOCINFO_COMMENTS, aInfo.aComment );
pObjShell->SetModified();
const SfxPoolItem* aItems[2];
aItems[0] = &aComment;
aItems[1] = nullptr;
m_pViewFrame->GetBindings().ExecuteSynchron( SID_SAVEDOC, aItems );
m_xVersionBox->freeze();
m_xVersionBox->clear();
m_xVersionBox->thaw();
Init_Impl();
}
}
else if (&rButton == m_xDeleteButton.get() && nEntry != -1)
{
SfxVersionInfo* pInfo = weld::fromId<SfxVersionInfo*>(m_xVersionBox->get_id(nEntry));
pObjShell->GetMedium()->RemoveVersion_Impl(pInfo->aName);
pObjShell->SetModified();
m_xVersionBox->freeze();
m_xVersionBox->clear();
m_xVersionBox->thaw();
Init_Impl();
}
else if (&rButton == m_xOpenButton.get() && nEntry != -1)
{
Open_Impl();
}
else if (&rButton == m_xViewButton.get() && nEntry != -1)
{
SfxVersionInfo* pInfo = weld::fromId<SfxVersionInfo*>(m_xVersionBox->get_id(nEntry));
SfxViewVersionDialog_Impl aDlg(m_xDialog.get(), *pInfo, false);
aDlg.run();
}
else if (&rButton == m_xCompareButton.get() && nEntry != -1)
{
SfxAllItemSet aSet( pObjShell->GetPool() );
aSet.Put(SfxInt16Item(SID_VERSION, nEntry + 1));
aSet.Put(SfxStringItem(SID_FILE_NAME, pObjShell->GetMedium()->GetName()));
SfxItemSet& rSet = pObjShell->GetMedium()->GetItemSet();
const SfxStringItem* pFilterItem = rSet.GetItem(SID_FILTER_NAME, false);
const SfxStringItem* pFilterOptItem = rSet.GetItem(SID_FILE_FILTEROPTIONS, false);
if ( pFilterItem )
aSet.Put( *pFilterItem );
if ( pFilterOptItem )
aSet.Put( *pFilterOptItem );
m_pViewFrame->GetDispatcher()->Execute( SID_DOCUMENT_COMPARE, SfxCallMode::ASYNCHRON, aSet );
m_xDialog->response(RET_CLOSE);
}
else if (&rButton == m_xCmisButton.get())
{
SfxCmisVersionsDialog aDlg(m_xDialog.get(), m_pViewFrame);
aDlg.run();
}
}
IMPL_LINK(SfxVersionDialog, ToggleHdl_Impl, weld::Toggleable&, rButton, void)
{
if (&rButton == m_xSaveCheckBox.get())
{
m_bIsSaveVersionOnClose = m_xSaveCheckBox->get_active();
}
}
SfxViewVersionDialog_Impl::SfxViewVersionDialog_Impl(weld::Window *pParent, SfxVersionInfo& rInfo, bool bEdit)
: SfxDialogController(pParent, "sfx/ui/versioncommentdialog.ui", "VersionCommentDialog")
, m_rInfo(rInfo)
, m_xDateTimeText(m_xBuilder->weld_label("timestamp"))
, m_xSavedByText(m_xBuilder->weld_label("author"))
, m_xEdit(m_xBuilder->weld_text_view("textview"))
, m_xOKButton(m_xBuilder->weld_button("ok"))
, m_xCancelButton(m_xBuilder->weld_button("cancel"))
, m_xCloseButton(m_xBuilder->weld_button("close"))
{
OUString sAuthor = rInfo.aAuthor.isEmpty() ? SfxResId(STR_NO_NAME_SET) : rInfo.aAuthor;
const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
m_xDateTimeText->set_label(m_xDateTimeText->get_label() + formatDateTime(rInfo.aCreationDate, rLocaleWrapper, false));
m_xSavedByText->set_label(m_xSavedByText->get_label() + sAuthor);
m_xEdit->set_text(rInfo.aComment);
m_xEdit->set_size_request(40 * m_xEdit->get_approximate_digit_width(),
7 * m_xEdit->get_text_height());
m_xOKButton->connect_clicked(LINK(this, SfxViewVersionDialog_Impl, ButtonHdl));
if (!bEdit)
{
m_xOKButton->hide();
m_xCancelButton->hide();
m_xEdit->set_editable(false);
m_xDialog->set_title(SfxResId(STR_VIEWVERSIONCOMMENT));
m_xCloseButton->grab_focus();
}
else
{
m_xDateTimeText->hide();
m_xCloseButton->hide();
m_xEdit->grab_focus();
}
}
IMPL_LINK(SfxViewVersionDialog_Impl, ButtonHdl, weld::Button&, rButton, void)
{
assert(&rButton == m_xOKButton.get());
(void)rButton;
m_rInfo.aComment = m_xEdit->get_text();
m_xDialog->response(RET_OK);
}
SfxCmisVersionsDialog::SfxCmisVersionsDialog(weld::Window* pParent, SfxViewFrame* pVwFrame)
: SfxDialogController(pParent, "sfx/ui/versionscmis.ui", "VersionsCmisDialog")
, m_pViewFrame(pVwFrame)
, m_xVersionBox(m_xBuilder->weld_tree_view("versions"))
{
m_xVersionBox->set_size_request(m_xVersionBox->get_approximate_digit_width() * 90,
m_xVersionBox->get_height_rows(15));
setColSizes(*m_xVersionBox);
m_xVersionBox->grab_focus();
OUString sText = m_xDialog->get_title() +
" " + m_pViewFrame->GetObjectShell()->GetTitle();
m_xDialog->set_title(sText);
LoadVersions();
}
SfxCmisVersionsDialog::~SfxCmisVersionsDialog()
{
}
void SfxCmisVersionsDialog::LoadVersions()
{
SfxObjectShell *pObjShell = m_pViewFrame->GetObjectShell();
uno::Sequence < document::CmisVersion > aVersions = pObjShell->GetCmisVersions( );
m_pTable.reset(new SfxVersionTableDtor( aVersions ));
for (size_t n = 0; n < m_pTable->size(); ++n)
{
SfxVersionInfo *pInfo = m_pTable->at( n );
OUString aEntry = formatDateTime(pInfo->aCreationDate, Application::GetSettings().GetLocaleDataWrapper(), false);
m_xVersionBox->append(weld::toId(pInfo), aEntry);
auto nLastRow = m_xVersionBox->n_children() - 1;
m_xVersionBox->set_text(nLastRow, pInfo->aAuthor, 1);
m_xVersionBox->set_text(nLastRow, ConvertWhiteSpaces_Impl(pInfo->aComment), 2);
}
if (auto nCount = m_pTable->size())
m_xVersionBox->select(nCount - 1);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */