diff options
Diffstat (limited to 'sc/source/ui/miscdlgs/conflictsdlg.cxx')
-rw-r--r-- | sc/source/ui/miscdlgs/conflictsdlg.cxx | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/sc/source/ui/miscdlgs/conflictsdlg.cxx b/sc/source/ui/miscdlgs/conflictsdlg.cxx new file mode 100644 index 000000000..3a48325aa --- /dev/null +++ b/sc/source/ui/miscdlgs/conflictsdlg.cxx @@ -0,0 +1,643 @@ +/* -*- 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 <comphelper/string.hxx> +#include <osl/diagnose.h> + +#include <conflictsdlg.hxx> +#include <o3tl/safeint.hxx> +#include <strings.hrc> +#include <scresid.hxx> +#include <viewdata.hxx> +#include <dbfunc.hxx> +#include <chgtrack.hxx> + +// struct ScConflictsListEntry + +bool ScConflictsListEntry::HasSharedAction( sal_uLong nSharedAction ) const +{ + auto aEnd = maSharedActions.cend(); + auto aItr = std::find(maSharedActions.cbegin(), aEnd, nSharedAction); + + return aItr != aEnd; +} + +bool ScConflictsListEntry::HasOwnAction( sal_uLong nOwnAction ) const +{ + auto aEnd = maOwnActions.cend(); + auto aItr = std::find(maOwnActions.cbegin(), aEnd, nOwnAction); + + return aItr != aEnd; +} + + +bool ScConflictsListHelper::HasOwnAction( ScConflictsList& rConflictsList, sal_uLong nOwnAction ) +{ + return std::any_of(rConflictsList.begin(), rConflictsList.end(), + [nOwnAction](ScConflictsListEntry& rConflict) { return rConflict.HasOwnAction( nOwnAction ); }); +} + +ScConflictsListEntry* ScConflictsListHelper::GetSharedActionEntry( ScConflictsList& rConflictsList, sal_uLong nSharedAction ) +{ + auto aEnd = rConflictsList.end(); + auto aItr = std::find_if(rConflictsList.begin(), aEnd, + [nSharedAction](ScConflictsListEntry& rConflict) { return rConflict.HasSharedAction( nSharedAction ); }); + + if (aItr != aEnd) + return &(*aItr); + + return nullptr; +} + +ScConflictsListEntry* ScConflictsListHelper::GetOwnActionEntry( ScConflictsList& rConflictsList, sal_uLong nOwnAction ) +{ + auto aEnd = rConflictsList.end(); + auto aItr = std::find_if(rConflictsList.begin(), aEnd, + [nOwnAction](ScConflictsListEntry& rConflict) { return rConflict.HasOwnAction( nOwnAction ); }); + + if (aItr != aEnd) + return &(*aItr); + + return nullptr; +} + +void ScConflictsListHelper::Transform_Impl( std::vector<sal_uLong>& rActionList, ScChangeActionMergeMap* pMergeMap ) +{ + if ( !pMergeMap ) + { + return; + } + + for ( auto aItr = rActionList.begin(); aItr != rActionList.end(); ) + { + ScChangeActionMergeMap::iterator aItrMap = pMergeMap->find( *aItr ); + if ( aItrMap != pMergeMap->end() ) + { + *aItr = aItrMap->second; + ++aItr; + } + else + { + aItr = rActionList.erase( aItr ); + OSL_FAIL( "ScConflictsListHelper::Transform_Impl: erased action from conflicts list!" ); + } + } +} + +void ScConflictsListHelper::TransformConflictsList( ScConflictsList& rConflictsList, + ScChangeActionMergeMap* pSharedMap, ScChangeActionMergeMap* pOwnMap ) +{ + for ( auto& rConflictEntry : rConflictsList ) + { + if ( pSharedMap ) + { + ScConflictsListHelper::Transform_Impl( rConflictEntry.maSharedActions, pSharedMap ); + } + + if ( pOwnMap ) + { + ScConflictsListHelper::Transform_Impl( rConflictEntry.maOwnActions, pOwnMap ); + } + } +} + + +ScConflictsFinder::ScConflictsFinder( ScChangeTrack* pTrack, sal_uLong nStartShared, sal_uLong nEndShared, + sal_uLong nStartOwn, sal_uLong nEndOwn, ScConflictsList& rConflictsList ) + :mpTrack( pTrack ) + ,mnStartShared( nStartShared ) + ,mnEndShared( nEndShared ) + ,mnStartOwn( nStartOwn ) + ,mnEndOwn( nEndOwn ) + ,mrConflictsList( rConflictsList ) +{ +} + +bool ScConflictsFinder::DoActionsIntersect( const ScChangeAction* pAction1, const ScChangeAction* pAction2 ) +{ + return pAction1 && pAction2 && pAction1->GetBigRange().Intersects( pAction2->GetBigRange() ); +} + +ScConflictsListEntry* ScConflictsFinder::GetIntersectingEntry( const ScChangeAction* pAction ) const +{ + auto doActionsIntersect = [this, pAction](const sal_uLong& aAction) { return DoActionsIntersect( mpTrack->GetAction( aAction ), pAction ); }; + + for ( auto& rConflict : mrConflictsList ) + { + if (std::any_of( rConflict.maSharedActions.cbegin(), rConflict.maSharedActions.cend(), doActionsIntersect )) + return &rConflict; + + if (std::any_of( rConflict.maOwnActions.cbegin(), rConflict.maOwnActions.cend(), doActionsIntersect )) + return &rConflict; + } + + return nullptr; +} + +ScConflictsListEntry& ScConflictsFinder::GetEntry( sal_uLong nSharedAction, const std::vector<sal_uLong>& rOwnActions ) +{ + // try to get a list entry which already contains the shared action + ScConflictsListEntry* pEntry = ScConflictsListHelper::GetSharedActionEntry( mrConflictsList, nSharedAction ); + if ( pEntry ) + { + return *pEntry; + } + + // try to get a list entry for which the shared action intersects with any + // other action of this entry + pEntry = GetIntersectingEntry( mpTrack->GetAction( nSharedAction ) ); + if ( pEntry ) + { + pEntry->maSharedActions.push_back( nSharedAction ); + return *pEntry; + } + + // try to get a list entry for which any of the own actions intersects with + // any other action of this entry + for ( auto& rOwnAction : rOwnActions ) + { + pEntry = GetIntersectingEntry( mpTrack->GetAction( rOwnAction ) ); + if ( pEntry ) + { + pEntry->maSharedActions.push_back( nSharedAction ); + return *pEntry; + } + } + + // if no entry was found, create a new one + ScConflictsListEntry aEntry; + aEntry.meConflictAction = SC_CONFLICT_ACTION_NONE; + aEntry.maSharedActions.push_back( nSharedAction ); + mrConflictsList.push_back( aEntry ); + return mrConflictsList.back(); +} + +bool ScConflictsFinder::Find() +{ + if ( !mpTrack ) + { + return false; + } + + bool bReturn = false; + ScChangeAction* pSharedAction = mpTrack->GetAction( mnStartShared ); + while ( pSharedAction && pSharedAction->GetActionNumber() <= mnEndShared ) + { + std::vector<sal_uLong> aOwnActions; + ScChangeAction* pOwnAction = mpTrack->GetAction( mnStartOwn ); + while ( pOwnAction && pOwnAction->GetActionNumber() <= mnEndOwn ) + { + if ( DoActionsIntersect( pSharedAction, pOwnAction ) ) + { + aOwnActions.push_back( pOwnAction->GetActionNumber() ); + } + pOwnAction = pOwnAction->GetNext(); + } + + if ( !aOwnActions.empty() ) + { + ScConflictsListEntry& rEntry = GetEntry(pSharedAction->GetActionNumber(), aOwnActions); + for ( const auto& aOwnAction : aOwnActions ) + { + if (!ScConflictsListHelper::HasOwnAction(mrConflictsList, aOwnAction)) + { + rEntry.maOwnActions.push_back(aOwnAction); + } + } + bReturn = true; + } + + pSharedAction = pSharedAction->GetNext(); + } + + return bReturn; +} + + +ScConflictsResolver::ScConflictsResolver( ScChangeTrack* pTrack, ScConflictsList& rConflictsList ) + :mpTrack ( pTrack ) + ,mrConflictsList ( rConflictsList ) +{ + OSL_ENSURE( mpTrack, "ScConflictsResolver CTOR: mpTrack is null!" ); +} + +void ScConflictsResolver::HandleAction( ScChangeAction* pAction, bool bIsSharedAction, + bool bHandleContentAction, bool bHandleNonContentAction ) +{ + if ( !mpTrack || !pAction ) + { + return; + } + + if ( bIsSharedAction ) + { + ScConflictsListEntry* pConflictEntry = ScConflictsListHelper::GetSharedActionEntry( + mrConflictsList, pAction->GetActionNumber() ); + if ( pConflictEntry ) + { + ScConflictAction eConflictAction = pConflictEntry->meConflictAction; + if ( eConflictAction == SC_CONFLICT_ACTION_KEEP_MINE ) + { + if ( pAction->GetType() == SC_CAT_CONTENT ) + { + if ( bHandleContentAction ) + { + mpTrack->Reject( pAction ); + } + } + else + { + if ( bHandleNonContentAction ) + { + mpTrack->Reject( pAction ); + } + } + } + } + } + else + { + ScConflictsListEntry* pConflictEntry = ScConflictsListHelper::GetOwnActionEntry( + mrConflictsList, pAction->GetActionNumber() ); + if ( pConflictEntry ) + { + ScConflictAction eConflictAction = pConflictEntry->meConflictAction; + if ( eConflictAction == SC_CONFLICT_ACTION_KEEP_MINE ) + { + if ( pAction->GetType() == SC_CAT_CONTENT ) + { + if ( bHandleContentAction ) + { + // do nothing + //mpTrack->SelectContent( pAction ); + } + } + else + { + if ( bHandleNonContentAction ) + { + // do nothing + //mpTrack->Accept( pAction ); + } + } + } + else if ( eConflictAction == SC_CONFLICT_ACTION_KEEP_OTHER ) + { + if ( pAction->GetType() == SC_CAT_CONTENT ) + { + if ( bHandleContentAction ) + { + mpTrack->Reject( pAction ); + } + } + else + { + if ( bHandleNonContentAction ) + { + mpTrack->Reject( pAction ); + } + } + } + } + } +} + + +ScConflictsDlg::ScConflictsDlg(weld::Window* pParent, ScViewData* pViewData, ScDocument* pSharedDoc, ScConflictsList& rConflictsList) + : GenericDialogController(pParent, "modules/scalc/ui/conflictsdialog.ui", "ConflictsDialog") + , maStrUnknownUser ( ScResId( STR_UNKNOWN_USER_CONFLICT ) ) + , mpViewData ( pViewData ) + , mpOwnDoc ( nullptr ) + , mpOwnTrack ( nullptr ) + , mpSharedDoc ( pSharedDoc ) + , mpSharedTrack ( nullptr ) + , mrConflictsList ( rConflictsList ) + , maSelectionIdle ( "ScConflictsDlg maSelectionIdle" ) + , mbInSelectHdl ( false ) + , m_xBtnKeepMine(m_xBuilder->weld_button("keepmine")) + , m_xBtnKeepOther(m_xBuilder->weld_button("keepother")) + , m_xBtnKeepAllMine(m_xBuilder->weld_button("keepallmine")) + , m_xBtnKeepAllOthers(m_xBuilder->weld_button("keepallothers")) + , m_xLbConflicts(new SvxRedlinTable(m_xBuilder->weld_tree_view("container"), nullptr)) +{ + OSL_ENSURE( mpViewData, "ScConflictsDlg CTOR: mpViewData is null!" ); + mpOwnDoc = ( mpViewData ? &mpViewData->GetDocument() : nullptr ); + OSL_ENSURE( mpOwnDoc, "ScConflictsDlg CTOR: mpOwnDoc is null!" ); + mpOwnTrack = ( mpOwnDoc ? mpOwnDoc->GetChangeTrack() : nullptr ); + OSL_ENSURE( mpOwnTrack, "ScConflictsDlg CTOR: mpOwnTrack is null!" ); + OSL_ENSURE( mpSharedDoc, "ScConflictsDlg CTOR: mpSharedDoc is null!" ); + mpSharedTrack = ( mpSharedDoc ? mpSharedDoc->GetChangeTrack() : nullptr ); + OSL_ENSURE( mpSharedTrack, "ScConflictsDlg CTOR: mpSharedTrack is null!" ); + + weld::TreeView& rTreeView = m_xLbConflicts->GetWidget(); + + auto nDigitWidth = rTreeView.get_approximate_digit_width(); + std::vector<int> aWidths + { + o3tl::narrowing<int>(nDigitWidth * 60), + o3tl::narrowing<int>(nDigitWidth * 20) + }; + rTreeView.set_column_fixed_widths(aWidths); + + rTreeView.set_selection_mode(SelectionMode::Multiple); + rTreeView.set_size_request(-1, rTreeView.get_height_rows(16)); + + maSelectionIdle.SetInvokeHandler( LINK( this, ScConflictsDlg, UpdateSelectionHdl ) ); + + rTreeView.connect_changed(LINK(this, ScConflictsDlg, SelectHandle)); + + m_xBtnKeepMine->connect_clicked( LINK( this, ScConflictsDlg, KeepMineHandle ) ); + m_xBtnKeepOther->connect_clicked( LINK( this, ScConflictsDlg, KeepOtherHandle ) ); + m_xBtnKeepAllMine->connect_clicked( LINK( this, ScConflictsDlg, KeepAllMineHandle ) ); + m_xBtnKeepAllOthers->connect_clicked( LINK( this, ScConflictsDlg, KeepAllOthersHandle ) ); + + UpdateView(); + + std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator()); + if (rTreeView.get_iter_first(*xEntry)) + rTreeView.select(*xEntry); +} + +ScConflictsDlg::~ScConflictsDlg() +{ +} + +OUString ScConflictsDlg::GetConflictString( const ScConflictsListEntry& rConflictEntry ) +{ + OUString aString; + if ( mpOwnTrack ) + { + const ScChangeAction* pAction = mpOwnTrack->GetAction( rConflictEntry.maOwnActions[ 0 ] ); + if ( pAction && mpOwnDoc ) + { + SCTAB nTab = pAction->GetBigRange().MakeRange( *mpOwnDoc ).aStart.Tab(); + mpOwnDoc->GetName( nTab, aString ); + } + } + return aString; +} + +void ScConflictsDlg::SetActionString(const ScChangeAction* pAction, ScDocument* pDoc, const weld::TreeIter& rEntry) +{ + OSL_ENSURE( pAction, "ScConflictsDlg::GetActionString(): pAction is null!" ); + OSL_ENSURE( pDoc, "ScConflictsDlg::GetActionString(): pDoc is null!" ); + if (!pAction || !pDoc) + return; + + weld::TreeView& rTreeView = m_xLbConflicts->GetWidget(); + OUString aDesc = pAction->GetDescription(*pDoc, true, false); + rTreeView.set_text(rEntry, aDesc, 0); + + OUString aUser = comphelper::string::strip(pAction->GetUser(), ' '); + if ( aUser.isEmpty() ) + { + aUser = maStrUnknownUser; + } + rTreeView.set_text(rEntry, aUser, 1); + + DateTime aDateTime = pAction->GetDateTime(); + OUString aString = ScGlobal::getLocaleData().getDate( aDateTime ) + " " + + ScGlobal::getLocaleData().getTime( aDateTime, false ); + rTreeView.set_text(rEntry, aString, 2); +} + +void ScConflictsDlg::HandleListBoxSelection() +{ + weld::TreeView& rTreeView = m_xLbConflicts->GetWidget(); + std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator()); + bool bSelEntry = rTreeView.get_cursor(xEntry.get()); + if (!bSelEntry) + bSelEntry = rTreeView.get_selected(xEntry.get()); + if (!bSelEntry) + return; + + bool bSelectHandle = rTreeView.is_selected(*xEntry); + + while (rTreeView.get_iter_depth(*xEntry)) + rTreeView.iter_parent(*xEntry); + + if (bSelectHandle) + rTreeView.unselect_all(); + if (!rTreeView.is_selected(*xEntry)) + rTreeView.select(*xEntry); + if (rTreeView.iter_children(*xEntry)) + { + do + { + if (!rTreeView.is_selected(*xEntry)) + rTreeView.select(*xEntry); + } while (rTreeView.iter_next_sibling(*xEntry)); + } +} + +IMPL_LINK_NOARG(ScConflictsDlg, SelectHandle, weld::TreeView&, void) +{ + if (mbInSelectHdl) + return; + + mbInSelectHdl = true; + HandleListBoxSelection(); + maSelectionIdle.Start(); + mbInSelectHdl = false; +} + +IMPL_LINK_NOARG(ScConflictsDlg, UpdateSelectionHdl, Timer *, void) +{ + if ( !mpViewData || !mpOwnDoc ) + { + return; + } + + ScTabView* pTabView = mpViewData->GetView(); + pTabView->DoneBlockMode(); + + std::vector<const ScChangeAction*> aActions; + + weld::TreeView& rTreeView = m_xLbConflicts->GetWidget(); + rTreeView.selected_foreach([&rTreeView, &aActions](weld::TreeIter& rEntry){ + if (rTreeView.get_iter_depth(rEntry)) + { + RedlinData* pUserData = weld::fromId<RedlinData*>(rTreeView.get_id(rEntry)); + if (pUserData) + { + ScChangeAction* pAction = static_cast< ScChangeAction* >( pUserData->pData ); + if ( pAction && ( pAction->GetType() != SC_CAT_DELETE_TABS ) && + ( pAction->IsClickable() || pAction->IsVisible() ) ) + { + aActions.push_back(pAction); + } + } + } + return false; + }); + + bool bContMark = false; + for (size_t i = 0, nCount = aActions.size(); i < nCount; ++i) + { + const ScBigRange& rBigRange = aActions[i]->GetBigRange(); + if (rBigRange.IsValid(*mpOwnDoc)) + { + bool bSetCursor = i == nCount - 1; + pTabView->MarkRange(rBigRange.MakeRange( *mpOwnDoc ), bSetCursor, bContMark); + bContMark = true; + } + } +} + +void ScConflictsDlg::SetConflictAction(const weld::TreeIter& rRootEntry, ScConflictAction eConflictAction) +{ + weld::TreeView& rTreeView = m_xLbConflicts->GetWidget(); + RedlinData* pUserData = weld::fromId<RedlinData*>(rTreeView.get_id(rRootEntry)); + ScConflictsListEntry* pConflictEntry = static_cast< ScConflictsListEntry* >( pUserData ? pUserData->pData : nullptr ); + if ( pConflictEntry ) + { + pConflictEntry->meConflictAction = eConflictAction; + } +} + +void ScConflictsDlg::KeepHandler(bool bMine) +{ + weld::TreeView& rTreeView = m_xLbConflicts->GetWidget(); + std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator()); + if (!rTreeView.get_selected(xEntry.get())) + return; + + while (rTreeView.get_iter_depth(*xEntry)) + rTreeView.iter_parent(*xEntry); + + m_xDialog->set_busy_cursor(true); + ScConflictAction eConflictAction = ( bMine ? SC_CONFLICT_ACTION_KEEP_MINE : SC_CONFLICT_ACTION_KEEP_OTHER ); + SetConflictAction(*xEntry, eConflictAction); + rTreeView.remove(*xEntry); + m_xDialog->set_busy_cursor(false); + if (rTreeView.n_children() == 0) + m_xDialog->response(RET_OK); +} + +void ScConflictsDlg::KeepAllHandler( bool bMine ) +{ + weld::TreeView& rTreeView = m_xLbConflicts->GetWidget(); + std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator()); + if (!rTreeView.get_iter_first(*xEntry)) + return; + + while (rTreeView.get_iter_depth(*xEntry)) + rTreeView.iter_parent(*xEntry); + + m_xDialog->set_busy_cursor(true); + + ScConflictAction eConflictAction = ( bMine ? SC_CONFLICT_ACTION_KEEP_MINE : SC_CONFLICT_ACTION_KEEP_OTHER ); + do + { + SetConflictAction(*xEntry, eConflictAction); + } while (rTreeView.iter_next_sibling(*xEntry)); + + rTreeView.freeze(); + rTreeView.clear(); + rTreeView.thaw(); + + m_xDialog->set_busy_cursor(false); + + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(ScConflictsDlg, KeepMineHandle, weld::Button&, void) +{ + KeepHandler( true ); +} + +IMPL_LINK_NOARG(ScConflictsDlg, KeepOtherHandle, weld::Button&, void) +{ + KeepHandler( false ); +} + +IMPL_LINK_NOARG(ScConflictsDlg, KeepAllMineHandle, weld::Button&, void) +{ + KeepAllHandler( true ); +} + +IMPL_LINK_NOARG(ScConflictsDlg, KeepAllOthersHandle, weld::Button&, void) +{ + KeepAllHandler( false ); +} + +void ScConflictsDlg::UpdateView() +{ + weld::TreeView& rTreeView = m_xLbConflicts->GetWidget(); + for ( ScConflictsListEntry& rConflictEntry : mrConflictsList ) + { + if (rConflictEntry.meConflictAction == SC_CONFLICT_ACTION_NONE) + { + std::unique_ptr<RedlinData> pRootUserData(new RedlinData()); + pRootUserData->pData = static_cast<void*>(&rConflictEntry); + OUString sString(GetConflictString(rConflictEntry)); + OUString sId(weld::toId(pRootUserData.release())); + std::unique_ptr<weld::TreeIter> xRootEntry(rTreeView.make_iterator()); + std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator()); + rTreeView.insert(nullptr, -1, &sString, &sId, nullptr, nullptr, false, xRootEntry.get()); + + for ( const auto& aSharedAction : rConflictEntry.maSharedActions ) + { + ScChangeAction* pAction = mpSharedTrack ? mpSharedTrack->GetAction(aSharedAction) : nullptr; + if ( pAction ) + { + // only display shared top content entries + if ( pAction->GetType() == SC_CAT_CONTENT ) + { + ScChangeActionContent* pNextContent = dynamic_cast<ScChangeActionContent&>(*pAction).GetNextContent(); + if ( pNextContent && rConflictEntry.HasSharedAction( pNextContent->GetActionNumber() ) ) + { + continue; + } + } + + rTreeView.insert(xRootEntry.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xEntry.get()); + SetActionString(pAction, mpSharedDoc, *xEntry); + } + } + + for ( const auto& aOwnAction : rConflictEntry.maOwnActions ) + { + ScChangeAction* pAction = mpOwnTrack ? mpOwnTrack->GetAction(aOwnAction) : nullptr; + if ( pAction ) + { + // only display own top content entries + if ( pAction->GetType() == SC_CAT_CONTENT ) + { + ScChangeActionContent* pNextContent = dynamic_cast<ScChangeActionContent&>(*pAction).GetNextContent(); + if ( pNextContent && rConflictEntry.HasOwnAction( pNextContent->GetActionNumber() ) ) + { + continue; + } + } + + std::unique_ptr<RedlinData> pUserData(new RedlinData()); + pUserData->pData = static_cast< void* >( pAction ); + OUString aId(weld::toId(pUserData.release())); + rTreeView.insert(xRootEntry.get(), -1, nullptr, &aId, nullptr, nullptr, false, xEntry.get()); + SetActionString(pAction, mpOwnDoc, *xEntry); + } + } + + rTreeView.expand_row(*xRootEntry); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |