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
, "modules/scalc/ui/conflictsdialog.ui", "ConflictsDialog")
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("keepmine"))
334 , m_xBtnKeepOther(m_xBuilder
->weld_button("keepother"))
335 , m_xBtnKeepAllMine(m_xBuilder
->weld_button("keepallmine"))
336 , m_xBtnKeepAllOthers(m_xBuilder
->weld_button("keepallothers"))
337 , m_xLbConflicts(new SvxRedlinTable(m_xBuilder
->weld_tree_view("container"), nullptr))
339 OSL_ENSURE( mpViewData
, "ScConflictsDlg CTOR: mpViewData is null!" );
340 mpOwnDoc
= ( mpViewData
? &mpViewData
->GetDocument() : nullptr );
341 OSL_ENSURE( mpOwnDoc
, "ScConflictsDlg CTOR: mpOwnDoc is null!" );
342 mpOwnTrack
= ( mpOwnDoc
? mpOwnDoc
->GetChangeTrack() : nullptr );
343 OSL_ENSURE( mpOwnTrack
, "ScConflictsDlg CTOR: mpOwnTrack is null!" );
344 OSL_ENSURE( mpSharedDoc
, "ScConflictsDlg CTOR: mpSharedDoc is null!" );
345 mpSharedTrack
= ( mpSharedDoc
? mpSharedDoc
->GetChangeTrack() : nullptr );
346 OSL_ENSURE( mpSharedTrack
, "ScConflictsDlg CTOR: mpSharedTrack is null!" );
348 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
350 auto nDigitWidth
= rTreeView
.get_approximate_digit_width();
351 std::vector
<int> aWidths
353 o3tl::narrowing
<int>(nDigitWidth
* 60),
354 o3tl::narrowing
<int>(nDigitWidth
* 20)
356 rTreeView
.set_column_fixed_widths(aWidths
);
358 rTreeView
.set_selection_mode(SelectionMode::Multiple
);
359 rTreeView
.set_size_request(-1, rTreeView
.get_height_rows(16));
361 maSelectionIdle
.SetInvokeHandler( LINK( this, ScConflictsDlg
, UpdateSelectionHdl
) );
363 rTreeView
.connect_changed(LINK(this, ScConflictsDlg
, SelectHandle
));
365 m_xBtnKeepMine
->connect_clicked( LINK( this, ScConflictsDlg
, KeepMineHandle
) );
366 m_xBtnKeepOther
->connect_clicked( LINK( this, ScConflictsDlg
, KeepOtherHandle
) );
367 m_xBtnKeepAllMine
->connect_clicked( LINK( this, ScConflictsDlg
, KeepAllMineHandle
) );
368 m_xBtnKeepAllOthers
->connect_clicked( LINK( this, ScConflictsDlg
, KeepAllOthersHandle
) );
372 std::unique_ptr
<weld::TreeIter
> xEntry(rTreeView
.make_iterator());
373 if (rTreeView
.get_iter_first(*xEntry
))
374 rTreeView
.select(*xEntry
);
377 ScConflictsDlg::~ScConflictsDlg()
381 OUString
ScConflictsDlg::GetConflictString( const ScConflictsListEntry
& rConflictEntry
)
386 const ScChangeAction
* pAction
= mpOwnTrack
->GetAction( rConflictEntry
.maOwnActions
[ 0 ] );
387 if ( pAction
&& mpOwnDoc
)
389 SCTAB nTab
= pAction
->GetBigRange().MakeRange( *mpOwnDoc
).aStart
.Tab();
390 mpOwnDoc
->GetName( nTab
, aString
);
396 void ScConflictsDlg::SetActionString(const ScChangeAction
* pAction
, ScDocument
* pDoc
, const weld::TreeIter
& rEntry
)
398 OSL_ENSURE( pAction
, "ScConflictsDlg::GetActionString(): pAction is null!" );
399 OSL_ENSURE( pDoc
, "ScConflictsDlg::GetActionString(): pDoc is null!" );
400 if (!pAction
|| !pDoc
)
403 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
404 OUString aDesc
= pAction
->GetDescription(*pDoc
, true, false);
405 rTreeView
.set_text(rEntry
, aDesc
, 0);
407 OUString aUser
= comphelper::string::strip(pAction
->GetUser(), ' ');
408 if ( aUser
.isEmpty() )
410 aUser
= maStrUnknownUser
;
412 rTreeView
.set_text(rEntry
, aUser
, 1);
414 DateTime aDateTime
= pAction
->GetDateTime();
415 OUString aString
= ScGlobal::getLocaleData().getDate( aDateTime
) + " " +
416 ScGlobal::getLocaleData().getTime( aDateTime
, false );
417 rTreeView
.set_text(rEntry
, aString
, 2);
420 void ScConflictsDlg::HandleListBoxSelection()
422 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
423 std::unique_ptr
<weld::TreeIter
> xEntry(rTreeView
.make_iterator());
424 bool bSelEntry
= rTreeView
.get_cursor(xEntry
.get());
426 bSelEntry
= rTreeView
.get_selected(xEntry
.get());
430 bool bSelectHandle
= rTreeView
.is_selected(*xEntry
);
432 while (rTreeView
.get_iter_depth(*xEntry
))
433 rTreeView
.iter_parent(*xEntry
);
436 rTreeView
.unselect_all();
437 if (!rTreeView
.is_selected(*xEntry
))
438 rTreeView
.select(*xEntry
);
439 if (rTreeView
.iter_children(*xEntry
))
443 if (!rTreeView
.is_selected(*xEntry
))
444 rTreeView
.select(*xEntry
);
445 } while (rTreeView
.iter_next_sibling(*xEntry
));
449 IMPL_LINK_NOARG(ScConflictsDlg
, SelectHandle
, weld::TreeView
&, void)
454 mbInSelectHdl
= true;
455 HandleListBoxSelection();
456 maSelectionIdle
.Start();
457 mbInSelectHdl
= false;
460 IMPL_LINK_NOARG(ScConflictsDlg
, UpdateSelectionHdl
, Timer
*, void)
462 if ( !mpViewData
|| !mpOwnDoc
)
467 ScTabView
* pTabView
= mpViewData
->GetView();
468 pTabView
->DoneBlockMode();
470 std::vector
<const ScChangeAction
*> aActions
;
472 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
473 rTreeView
.selected_foreach([&rTreeView
, &aActions
](weld::TreeIter
& rEntry
){
474 if (rTreeView
.get_iter_depth(rEntry
))
476 RedlinData
* pUserData
= weld::fromId
<RedlinData
*>(rTreeView
.get_id(rEntry
));
479 ScChangeAction
* pAction
= static_cast< ScChangeAction
* >( pUserData
->pData
);
480 if ( pAction
&& ( pAction
->GetType() != SC_CAT_DELETE_TABS
) &&
481 ( pAction
->IsClickable() || pAction
->IsVisible() ) )
483 aActions
.push_back(pAction
);
490 bool bContMark
= false;
491 for (size_t i
= 0, nCount
= aActions
.size(); i
< nCount
; ++i
)
493 const ScBigRange
& rBigRange
= aActions
[i
]->GetBigRange();
494 if (rBigRange
.IsValid(*mpOwnDoc
))
496 bool bSetCursor
= i
== nCount
- 1;
497 pTabView
->MarkRange(rBigRange
.MakeRange( *mpOwnDoc
), bSetCursor
, bContMark
);
503 void ScConflictsDlg::SetConflictAction(const weld::TreeIter
& rRootEntry
, ScConflictAction eConflictAction
)
505 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
506 RedlinData
* pUserData
= weld::fromId
<RedlinData
*>(rTreeView
.get_id(rRootEntry
));
507 ScConflictsListEntry
* pConflictEntry
= static_cast< ScConflictsListEntry
* >( pUserData
? pUserData
->pData
: nullptr );
508 if ( pConflictEntry
)
510 pConflictEntry
->meConflictAction
= eConflictAction
;
514 void ScConflictsDlg::KeepHandler(bool bMine
)
516 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
517 std::unique_ptr
<weld::TreeIter
> xEntry(rTreeView
.make_iterator());
518 if (!rTreeView
.get_selected(xEntry
.get()))
521 while (rTreeView
.get_iter_depth(*xEntry
))
522 rTreeView
.iter_parent(*xEntry
);
524 m_xDialog
->set_busy_cursor(true);
525 ScConflictAction eConflictAction
= ( bMine
? SC_CONFLICT_ACTION_KEEP_MINE
: SC_CONFLICT_ACTION_KEEP_OTHER
);
526 SetConflictAction(*xEntry
, eConflictAction
);
527 rTreeView
.remove(*xEntry
);
528 m_xDialog
->set_busy_cursor(false);
529 if (rTreeView
.n_children() == 0)
530 m_xDialog
->response(RET_OK
);
533 void ScConflictsDlg::KeepAllHandler( bool bMine
)
535 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
536 std::unique_ptr
<weld::TreeIter
> xEntry(rTreeView
.make_iterator());
537 if (!rTreeView
.get_iter_first(*xEntry
))
540 while (rTreeView
.get_iter_depth(*xEntry
))
541 rTreeView
.iter_parent(*xEntry
);
543 m_xDialog
->set_busy_cursor(true);
545 ScConflictAction eConflictAction
= ( bMine
? SC_CONFLICT_ACTION_KEEP_MINE
: SC_CONFLICT_ACTION_KEEP_OTHER
);
548 SetConflictAction(*xEntry
, eConflictAction
);
549 } while (rTreeView
.iter_next_sibling(*xEntry
));
555 m_xDialog
->set_busy_cursor(false);
557 m_xDialog
->response(RET_OK
);
560 IMPL_LINK_NOARG(ScConflictsDlg
, KeepMineHandle
, weld::Button
&, void)
565 IMPL_LINK_NOARG(ScConflictsDlg
, KeepOtherHandle
, weld::Button
&, void)
567 KeepHandler( false );
570 IMPL_LINK_NOARG(ScConflictsDlg
, KeepAllMineHandle
, weld::Button
&, void)
572 KeepAllHandler( true );
575 IMPL_LINK_NOARG(ScConflictsDlg
, KeepAllOthersHandle
, weld::Button
&, void)
577 KeepAllHandler( false );
580 void ScConflictsDlg::UpdateView()
582 weld::TreeView
& rTreeView
= m_xLbConflicts
->GetWidget();
583 for ( ScConflictsListEntry
& rConflictEntry
: mrConflictsList
)
585 if (rConflictEntry
.meConflictAction
== SC_CONFLICT_ACTION_NONE
)
587 std::unique_ptr
<RedlinData
> pRootUserData(new RedlinData());
588 pRootUserData
->pData
= static_cast<void*>(&rConflictEntry
);
589 OUString
sString(GetConflictString(rConflictEntry
));
590 OUString
sId(weld::toId(pRootUserData
.release()));
591 std::unique_ptr
<weld::TreeIter
> xRootEntry(rTreeView
.make_iterator());
592 std::unique_ptr
<weld::TreeIter
> xEntry(rTreeView
.make_iterator());
593 rTreeView
.insert(nullptr, -1, &sString
, &sId
, nullptr, nullptr, false, xRootEntry
.get());
595 for ( const auto& aSharedAction
: rConflictEntry
.maSharedActions
)
597 ScChangeAction
* pAction
= mpSharedTrack
? mpSharedTrack
->GetAction(aSharedAction
) : nullptr;
600 // only display shared top content entries
601 if ( pAction
->GetType() == SC_CAT_CONTENT
)
603 ScChangeActionContent
* pNextContent
= dynamic_cast<ScChangeActionContent
&>(*pAction
).GetNextContent();
604 if ( pNextContent
&& rConflictEntry
.HasSharedAction( pNextContent
->GetActionNumber() ) )
610 rTreeView
.insert(xRootEntry
.get(), -1, nullptr, nullptr, nullptr, nullptr, false, xEntry
.get());
611 SetActionString(pAction
, mpSharedDoc
, *xEntry
);
615 for ( const auto& aOwnAction
: rConflictEntry
.maOwnActions
)
617 ScChangeAction
* pAction
= mpOwnTrack
? mpOwnTrack
->GetAction(aOwnAction
) : nullptr;
620 // only display own top content entries
621 if ( pAction
->GetType() == SC_CAT_CONTENT
)
623 ScChangeActionContent
* pNextContent
= dynamic_cast<ScChangeActionContent
&>(*pAction
).GetNextContent();
624 if ( pNextContent
&& rConflictEntry
.HasOwnAction( pNextContent
->GetActionNumber() ) )
630 std::unique_ptr
<RedlinData
> pUserData(new RedlinData());
631 pUserData
->pData
= static_cast< void* >( pAction
);
632 OUString
aId(weld::toId(pUserData
.release()));
633 rTreeView
.insert(xRootEntry
.get(), -1, nullptr, &aId
, nullptr, nullptr, false, xEntry
.get());
634 SetActionString(pAction
, mpOwnDoc
, *xEntry
);
638 rTreeView
.expand_row(*xRootEntry
);
643 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */