1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <comphelper/string.hxx>
21 #include <osl/diagnose.h>
23 #include <conflictsdlg.hxx>
24 #include <o3tl/safeint.hxx>
25 #include <strings.hrc>
26 #include <scresid.hxx>
27 #include <viewdata.hxx>
29 #include <chgtrack.hxx>
31 // struct ScConflictsListEntry
33 bool ScConflictsListEntry::HasSharedAction( sal_uLong nSharedAction
) const
35 auto aEnd
= maSharedActions
.cend();
36 auto aItr
= std::find(maSharedActions
.cbegin(), aEnd
, nSharedAction
);
41 bool ScConflictsListEntry::HasOwnAction( sal_uLong nOwnAction
) const
43 auto aEnd
= maOwnActions
.cend();
44 auto aItr
= std::find(maOwnActions
.cbegin(), aEnd
, nOwnAction
);
50 bool ScConflictsListHelper::HasOwnAction( ScConflictsList
& rConflictsList
, sal_uLong nOwnAction
)
52 return std::any_of(rConflictsList
.begin(), rConflictsList
.end(),
53 [nOwnAction
](ScConflictsListEntry
& rConflict
) { return rConflict
.HasOwnAction( nOwnAction
); });
56 ScConflictsListEntry
* ScConflictsListHelper::GetSharedActionEntry( ScConflictsList
& rConflictsList
, sal_uLong nSharedAction
)
58 auto aEnd
= rConflictsList
.end();
59 auto aItr
= std::find_if(rConflictsList
.begin(), aEnd
,
60 [nSharedAction
](ScConflictsListEntry
& rConflict
) { return rConflict
.HasSharedAction( nSharedAction
); });
68 ScConflictsListEntry
* ScConflictsListHelper::GetOwnActionEntry( ScConflictsList
& rConflictsList
, sal_uLong nOwnAction
)
70 auto aEnd
= rConflictsList
.end();
71 auto aItr
= std::find_if(rConflictsList
.begin(), aEnd
,
72 [nOwnAction
](ScConflictsListEntry
& rConflict
) { return rConflict
.HasOwnAction( nOwnAction
); });
80 void ScConflictsListHelper::Transform_Impl( std::vector
<sal_uLong
>& rActionList
, ScChangeActionMergeMap
* pMergeMap
)
87 for ( auto aItr
= rActionList
.begin(); aItr
!= rActionList
.end(); )
89 ScChangeActionMergeMap::iterator aItrMap
= pMergeMap
->find( *aItr
);
90 if ( aItrMap
!= pMergeMap
->end() )
92 *aItr
= aItrMap
->second
;
97 aItr
= rActionList
.erase( aItr
);
98 OSL_FAIL( "ScConflictsListHelper::Transform_Impl: erased action from conflicts list!" );
103 void ScConflictsListHelper::TransformConflictsList( ScConflictsList
& rConflictsList
,
104 ScChangeActionMergeMap
* pSharedMap
, ScChangeActionMergeMap
* pOwnMap
)
106 for ( auto& rConflictEntry
: rConflictsList
)
110 ScConflictsListHelper::Transform_Impl( rConflictEntry
.maSharedActions
, pSharedMap
);
115 ScConflictsListHelper::Transform_Impl( rConflictEntry
.maOwnActions
, pOwnMap
);
121 ScConflictsFinder::ScConflictsFinder( ScChangeTrack
* pTrack
, sal_uLong nStartShared
, sal_uLong nEndShared
,
122 sal_uLong nStartOwn
, sal_uLong nEndOwn
, ScConflictsList
& rConflictsList
)
124 ,mnStartShared( nStartShared
)
125 ,mnEndShared( nEndShared
)
126 ,mnStartOwn( nStartOwn
)
128 ,mrConflictsList( rConflictsList
)
132 bool ScConflictsFinder::DoActionsIntersect( const ScChangeAction
* pAction1
, const ScChangeAction
* pAction2
)
134 return pAction1
&& pAction2
&& pAction1
->GetBigRange().Intersects( pAction2
->GetBigRange() );
137 ScConflictsListEntry
* ScConflictsFinder::GetIntersectingEntry( const ScChangeAction
* pAction
) const
139 auto doActionsIntersect
= [this, pAction
](const sal_uLong
& aAction
) { return DoActionsIntersect( mpTrack
->GetAction( aAction
), pAction
); };
141 for ( auto& rConflict
: mrConflictsList
)
143 if (std::any_of( rConflict
.maSharedActions
.cbegin(), rConflict
.maSharedActions
.cend(), doActionsIntersect
))
146 if (std::any_of( rConflict
.maOwnActions
.cbegin(), rConflict
.maOwnActions
.cend(), doActionsIntersect
))
153 ScConflictsListEntry
& ScConflictsFinder::GetEntry( sal_uLong nSharedAction
, const std::vector
<sal_uLong
>& rOwnActions
)
155 // try to get a list entry which already contains the shared action
156 ScConflictsListEntry
* pEntry
= ScConflictsListHelper::GetSharedActionEntry( mrConflictsList
, nSharedAction
);
162 // try to get a list entry for which the shared action intersects with any
163 // other action of this entry
164 pEntry
= GetIntersectingEntry( mpTrack
->GetAction( nSharedAction
) );
167 pEntry
->maSharedActions
.push_back( nSharedAction
);
171 // try to get a list entry for which any of the own actions intersects with
172 // any other action of this entry
173 for ( auto& rOwnAction
: rOwnActions
)
175 pEntry
= GetIntersectingEntry( mpTrack
->GetAction( rOwnAction
) );
178 pEntry
->maSharedActions
.push_back( nSharedAction
);
183 // if no entry was found, create a new one
184 ScConflictsListEntry aEntry
;
185 aEntry
.meConflictAction
= SC_CONFLICT_ACTION_NONE
;
186 aEntry
.maSharedActions
.push_back( nSharedAction
);
187 mrConflictsList
.push_back( aEntry
);
188 return mrConflictsList
.back();
191 bool ScConflictsFinder::Find()
198 bool bReturn
= false;
199 ScChangeAction
* pSharedAction
= mpTrack
->GetAction( mnStartShared
);
200 while ( pSharedAction
&& pSharedAction
->GetActionNumber() <= mnEndShared
)
202 std::vector
<sal_uLong
> aOwnActions
;
203 ScChangeAction
* pOwnAction
= mpTrack
->GetAction( mnStartOwn
);
204 while ( pOwnAction
&& pOwnAction
->GetActionNumber() <= mnEndOwn
)
206 if ( DoActionsIntersect( pSharedAction
, pOwnAction
) )
208 aOwnActions
.push_back( pOwnAction
->GetActionNumber() );
210 pOwnAction
= pOwnAction
->GetNext();
213 if ( !aOwnActions
.empty() )
215 ScConflictsListEntry
& rEntry
= GetEntry(pSharedAction
->GetActionNumber(), aOwnActions
);
216 for ( const auto& aOwnAction
: aOwnActions
)
218 if (!ScConflictsListHelper::HasOwnAction(mrConflictsList
, aOwnAction
))
220 rEntry
.maOwnActions
.push_back(aOwnAction
);
226 pSharedAction
= pSharedAction
->GetNext();
233 ScConflictsResolver::ScConflictsResolver( ScChangeTrack
* pTrack
, ScConflictsList
& rConflictsList
)
235 ,mrConflictsList ( rConflictsList
)
237 OSL_ENSURE( mpTrack
, "ScConflictsResolver CTOR: mpTrack is null!" );
240 void ScConflictsResolver::HandleAction( ScChangeAction
* pAction
, bool bIsSharedAction
,
241 bool bHandleContentAction
, bool bHandleNonContentAction
)
243 if ( !mpTrack
|| !pAction
)
248 if ( bIsSharedAction
)
250 ScConflictsListEntry
* pConflictEntry
= ScConflictsListHelper::GetSharedActionEntry(
251 mrConflictsList
, pAction
->GetActionNumber() );
252 if ( pConflictEntry
)
254 ScConflictAction eConflictAction
= pConflictEntry
->meConflictAction
;
255 if ( eConflictAction
== SC_CONFLICT_ACTION_KEEP_MINE
)
257 if ( pAction
->GetType() == SC_CAT_CONTENT
)
259 if ( bHandleContentAction
)
261 mpTrack
->Reject( pAction
);
266 if ( bHandleNonContentAction
)
268 mpTrack
->Reject( pAction
);
276 ScConflictsListEntry
* pConflictEntry
= ScConflictsListHelper::GetOwnActionEntry(
277 mrConflictsList
, pAction
->GetActionNumber() );
278 if ( pConflictEntry
)
280 ScConflictAction eConflictAction
= pConflictEntry
->meConflictAction
;
281 if ( eConflictAction
== SC_CONFLICT_ACTION_KEEP_MINE
)
283 if ( pAction
->GetType() == SC_CAT_CONTENT
)
285 if ( bHandleContentAction
)
288 //mpTrack->SelectContent( pAction );
293 if ( bHandleNonContentAction
)
296 //mpTrack->Accept( pAction );
300 else if ( eConflictAction
== SC_CONFLICT_ACTION_KEEP_OTHER
)
302 if ( pAction
->GetType() == SC_CAT_CONTENT
)
304 if ( bHandleContentAction
)
306 mpTrack
->Reject( pAction
);
311 if ( bHandleNonContentAction
)
313 mpTrack
->Reject( pAction
);
322 ScConflictsDlg::ScConflictsDlg(weld::Window
* pParent
, ScViewData
* pViewData
, ScDocument
* pSharedDoc
, ScConflictsList
& rConflictsList
)
323 : GenericDialogController(pParent
, u
"modules/scalc/ui/conflictsdialog.ui"_ustr
, u
"ConflictsDialog"_ustr
)
324 , maStrUnknownUser ( ScResId( STR_UNKNOWN_USER_CONFLICT
) )
325 , mpViewData ( pViewData
)
326 , mpOwnDoc ( nullptr )
327 , mpOwnTrack ( nullptr )
328 , mpSharedDoc ( pSharedDoc
)
329 , mpSharedTrack ( nullptr )
330 , mrConflictsList ( rConflictsList
)
331 , maSelectionIdle ( "ScConflictsDlg maSelectionIdle" )
332 , mbInSelectHdl ( false )
333 , m_xBtnKeepMine(m_xBuilder
->weld_button(u
"keepmine"_ustr
))
334 , m_xBtnKeepOther(m_xBuilder
->weld_button(u
"keepother"_ustr
))
335 , m_xBtnKeepAllMine(m_xBuilder
->weld_button(u
"keepallmine"_ustr
))
336 , m_xBtnKeepAllOthers(m_xBuilder
->weld_button(u
"keepallothers"_ustr
))
337 , m_xLbConflicts(new SvxRedlinTable(m_xBuilder
->weld_tree_view(u
"container"_ustr
), nullptr,
340 OSL_ENSURE( mpViewData
, "ScConflictsDlg CTOR: mpViewData is null!" );
341 mpOwnDoc
= ( mpViewData
? &mpViewData
->GetDocument() : nullptr );
342 OSL_ENSURE( mpOwnDoc
, "ScConflictsDlg CTOR: mpOwnDoc is null!" );
343 mpOwnTrack
= ( mpOwnDoc
? mpOwnDoc
->GetChangeTrack() : nullptr );
344 OSL_ENSURE( mpOwnTrack
, "ScConflictsDlg CTOR: mpOwnTrack is null!" );
345 OSL_ENSURE( mpSharedDoc
, "ScConflictsDlg CTOR: mpSharedDoc is null!" );
346 mpSharedTrack
= ( mpSharedDoc
? mpSharedDoc
->GetChangeTrack() : nullptr );
347 OSL_ENSURE( mpSharedTrack
, "ScConflictsDlg CTOR: mpSharedTrack is null!" );
349 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
351 auto nDigitWidth
= rTreeView
.get_approximate_digit_width();
352 std::vector
<int> aWidths
354 o3tl::narrowing
<int>(nDigitWidth
* 60),
355 o3tl::narrowing
<int>(nDigitWidth
* 20)
357 rTreeView
.set_column_fixed_widths(aWidths
);
359 rTreeView
.set_selection_mode(SelectionMode::Multiple
);
360 rTreeView
.set_size_request(-1, rTreeView
.get_height_rows(16));
362 maSelectionIdle
.SetInvokeHandler( LINK( this, ScConflictsDlg
, UpdateSelectionHdl
) );
364 rTreeView
.connect_selection_changed(LINK(this, ScConflictsDlg
, SelectHandle
));
366 m_xBtnKeepMine
->connect_clicked( LINK( this, ScConflictsDlg
, KeepMineHandle
) );
367 m_xBtnKeepOther
->connect_clicked( LINK( this, ScConflictsDlg
, KeepOtherHandle
) );
368 m_xBtnKeepAllMine
->connect_clicked( LINK( this, ScConflictsDlg
, KeepAllMineHandle
) );
369 m_xBtnKeepAllOthers
->connect_clicked( LINK( this, ScConflictsDlg
, KeepAllOthersHandle
) );
373 std::unique_ptr
<weld::TreeIter
> xEntry(rTreeView
.make_iterator());
374 if (rTreeView
.get_iter_first(*xEntry
))
375 rTreeView
.select(*xEntry
);
378 ScConflictsDlg::~ScConflictsDlg()
382 OUString
ScConflictsDlg::GetConflictString( const ScConflictsListEntry
& rConflictEntry
)
387 const ScChangeAction
* pAction
= mpOwnTrack
->GetAction( rConflictEntry
.maOwnActions
[ 0 ] );
388 if ( pAction
&& mpOwnDoc
)
390 SCTAB nTab
= pAction
->GetBigRange().MakeRange( *mpOwnDoc
).aStart
.Tab();
391 mpOwnDoc
->GetName( nTab
, aString
);
397 void ScConflictsDlg::SetActionString(const ScChangeAction
* pAction
, ScDocument
* pDoc
, const weld::TreeIter
& rEntry
)
399 OSL_ENSURE( pAction
, "ScConflictsDlg::GetActionString(): pAction is null!" );
400 OSL_ENSURE( pDoc
, "ScConflictsDlg::GetActionString(): pDoc is null!" );
401 if (!pAction
|| !pDoc
)
404 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
405 OUString aDesc
= pAction
->GetDescription(*pDoc
, true, false);
406 rTreeView
.set_text(rEntry
, aDesc
, 0);
408 OUString aUser
= comphelper::string::strip(pAction
->GetUser(), ' ');
409 if ( aUser
.isEmpty() )
411 aUser
= maStrUnknownUser
;
413 rTreeView
.set_text(rEntry
, aUser
, 1);
415 DateTime aDateTime
= pAction
->GetDateTime();
416 OUString aString
= ScGlobal::getLocaleData().getDate( aDateTime
) + " " +
417 ScGlobal::getLocaleData().getTime( aDateTime
, false );
418 rTreeView
.set_text(rEntry
, aString
, 2);
421 void ScConflictsDlg::HandleListBoxSelection()
423 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
424 std::unique_ptr
<weld::TreeIter
> xEntry(rTreeView
.make_iterator());
425 bool bSelEntry
= rTreeView
.get_cursor(xEntry
.get());
427 bSelEntry
= rTreeView
.get_selected(xEntry
.get());
431 bool bSelectHandle
= rTreeView
.is_selected(*xEntry
);
433 while (rTreeView
.get_iter_depth(*xEntry
))
434 rTreeView
.iter_parent(*xEntry
);
437 rTreeView
.unselect_all();
438 if (!rTreeView
.is_selected(*xEntry
))
439 rTreeView
.select(*xEntry
);
440 if (rTreeView
.iter_children(*xEntry
))
444 if (!rTreeView
.is_selected(*xEntry
))
445 rTreeView
.select(*xEntry
);
446 } while (rTreeView
.iter_next_sibling(*xEntry
));
450 IMPL_LINK_NOARG(ScConflictsDlg
, SelectHandle
, weld::TreeView
&, void)
455 mbInSelectHdl
= true;
456 HandleListBoxSelection();
457 maSelectionIdle
.Start();
458 mbInSelectHdl
= false;
461 IMPL_LINK_NOARG(ScConflictsDlg
, UpdateSelectionHdl
, Timer
*, void)
463 if ( !mpViewData
|| !mpOwnDoc
)
468 ScTabView
* pTabView
= mpViewData
->GetView();
469 pTabView
->DoneBlockMode();
471 std::vector
<const ScChangeAction
*> aActions
;
473 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
474 rTreeView
.selected_foreach([&rTreeView
, &aActions
](weld::TreeIter
& rEntry
){
475 if (rTreeView
.get_iter_depth(rEntry
))
477 RedlinData
* pUserData
= weld::fromId
<RedlinData
*>(rTreeView
.get_id(rEntry
));
480 ScChangeAction
* pAction
= static_cast< ScChangeAction
* >( pUserData
->pData
);
481 if ( pAction
&& ( pAction
->GetType() != SC_CAT_DELETE_TABS
) &&
482 ( pAction
->IsClickable() || pAction
->IsVisible() ) )
484 aActions
.push_back(pAction
);
491 bool bContMark
= false;
492 for (size_t i
= 0, nCount
= aActions
.size(); i
< nCount
; ++i
)
494 const ScBigRange
& rBigRange
= aActions
[i
]->GetBigRange();
495 if (rBigRange
.IsValid(*mpOwnDoc
))
497 bool bSetCursor
= i
== nCount
- 1;
498 pTabView
->MarkRange(rBigRange
.MakeRange( *mpOwnDoc
), bSetCursor
, bContMark
);
504 void ScConflictsDlg::SetConflictAction(const weld::TreeIter
& rRootEntry
, ScConflictAction eConflictAction
)
506 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
507 RedlinData
* pUserData
= weld::fromId
<RedlinData
*>(rTreeView
.get_id(rRootEntry
));
508 ScConflictsListEntry
* pConflictEntry
= static_cast< ScConflictsListEntry
* >( pUserData
? pUserData
->pData
: nullptr );
509 if ( pConflictEntry
)
511 pConflictEntry
->meConflictAction
= eConflictAction
;
515 void ScConflictsDlg::KeepHandler(bool bMine
)
517 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
518 std::unique_ptr
<weld::TreeIter
> xEntry(rTreeView
.make_iterator());
519 if (!rTreeView
.get_selected(xEntry
.get()))
522 while (rTreeView
.get_iter_depth(*xEntry
))
523 rTreeView
.iter_parent(*xEntry
);
525 m_xDialog
->set_busy_cursor(true);
526 ScConflictAction eConflictAction
= ( bMine
? SC_CONFLICT_ACTION_KEEP_MINE
: SC_CONFLICT_ACTION_KEEP_OTHER
);
527 SetConflictAction(*xEntry
, eConflictAction
);
528 rTreeView
.remove(*xEntry
);
529 m_xDialog
->set_busy_cursor(false);
530 if (rTreeView
.n_children() == 0)
531 m_xDialog
->response(RET_OK
);
534 void ScConflictsDlg::KeepAllHandler( bool bMine
)
536 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
537 std::unique_ptr
<weld::TreeIter
> xEntry(rTreeView
.make_iterator());
538 if (!rTreeView
.get_iter_first(*xEntry
))
541 while (rTreeView
.get_iter_depth(*xEntry
))
542 rTreeView
.iter_parent(*xEntry
);
544 m_xDialog
->set_busy_cursor(true);
546 ScConflictAction eConflictAction
= ( bMine
? SC_CONFLICT_ACTION_KEEP_MINE
: SC_CONFLICT_ACTION_KEEP_OTHER
);
549 SetConflictAction(*xEntry
, eConflictAction
);
550 } while (rTreeView
.iter_next_sibling(*xEntry
));
556 m_xDialog
->set_busy_cursor(false);
558 m_xDialog
->response(RET_OK
);
561 IMPL_LINK_NOARG(ScConflictsDlg
, KeepMineHandle
, weld::Button
&, void)
566 IMPL_LINK_NOARG(ScConflictsDlg
, KeepOtherHandle
, weld::Button
&, void)
568 KeepHandler( false );
571 IMPL_LINK_NOARG(ScConflictsDlg
, KeepAllMineHandle
, weld::Button
&, void)
573 KeepAllHandler( true );
576 IMPL_LINK_NOARG(ScConflictsDlg
, KeepAllOthersHandle
, weld::Button
&, void)
578 KeepAllHandler( false );
581 void ScConflictsDlg::UpdateView()
583 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
584 for ( ScConflictsListEntry
& rConflictEntry
: mrConflictsList
)
586 if (rConflictEntry
.meConflictAction
== SC_CONFLICT_ACTION_NONE
)
588 std::unique_ptr
<RedlinData
> pRootUserData(new RedlinData());
589 pRootUserData
->pData
= static_cast<void*>(&rConflictEntry
);
590 OUString
sString(GetConflictString(rConflictEntry
));
591 OUString
sId(weld::toId(pRootUserData
.release()));
592 std::unique_ptr
<weld::TreeIter
> xRootEntry(rTreeView
.make_iterator());
593 std::unique_ptr
<weld::TreeIter
> xEntry(rTreeView
.make_iterator());
594 rTreeView
.insert(nullptr, -1, &sString
, &sId
, nullptr, nullptr, false, xRootEntry
.get());
596 for ( const auto& aSharedAction
: rConflictEntry
.maSharedActions
)
598 ScChangeAction
* pAction
= mpSharedTrack
? mpSharedTrack
->GetAction(aSharedAction
) : nullptr;
601 // only display shared top content entries
602 if ( pAction
->GetType() == SC_CAT_CONTENT
)
604 ScChangeActionContent
* pNextContent
= dynamic_cast<ScChangeActionContent
&>(*pAction
).GetNextContent();
605 if ( pNextContent
&& rConflictEntry
.HasSharedAction( pNextContent
->GetActionNumber() ) )
611 rTreeView
.insert(xRootEntry
.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xEntry
.get());
612 SetActionString(pAction
, mpSharedDoc
, *xEntry
);
616 for ( const auto& aOwnAction
: rConflictEntry
.maOwnActions
)
618 ScChangeAction
* pAction
= mpOwnTrack
? mpOwnTrack
->GetAction(aOwnAction
) : nullptr;
621 // only display own top content entries
622 if ( pAction
->GetType() == SC_CAT_CONTENT
)
624 ScChangeActionContent
* pNextContent
= dynamic_cast<ScChangeActionContent
&>(*pAction
).GetNextContent();
625 if ( pNextContent
&& rConflictEntry
.HasOwnAction( pNextContent
->GetActionNumber() ) )
631 std::unique_ptr
<RedlinData
> pUserData(new RedlinData());
632 pUserData
->pData
= static_cast< void* >( pAction
);
633 OUString
aId(weld::toId(pUserData
.release()));
634 rTreeView
.insert(xRootEntry
.get(), -1, nullptr, &aId
, nullptr, nullptr, false, xEntry
.get());
635 SetActionString(pAction
, mpOwnDoc
, *xEntry
);
639 rTreeView
.expand_row(*xRootEntry
);
644 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */