namespace ui
{ /////////////////////////////////////////////////////////////////
+ /** Simple ResStatus backup stack.
+ * \ref restore simply rewinds all remembered status.
+ */
+ class StatusBackup
+ {
+ public:
+ typedef ResStatus::TransactByValue Causer;
+
+ public:
+ /** Backup status. */
+ ResStatus & backup( ResStatus & status_r )
+ {
+ _backup.push_back( status_r );
+ return status_r;
+ }
+ /** \overload */
+ ResStatus & backup( const PoolItem & pi_r )
+ { return backup( pi_r.status() ); }
+
+ /** Backup status. */
+ ResStatus & operator()( ResStatus & status_r )
+ { return backup( status_r ); }
+ /** \overload */
+ ResStatus & operator()( const PoolItem & pi_r )
+ { return backup( pi_r.status() ); }
+
+ /** Restore all status. */
+ bool restore()
+ {
+ for_( rit, _backup.rbegin(), _backup.rend() )
+ rit->replay();
+ return false; // restore is done on error - return restore();
+ }
+
+ public:
+ /** lowlevel \c ResStatus::setTransact */
+ bool setTransact( const PoolItem & pi_r, bool yesno_r, Causer causer_r )
+ { return backup( pi_r ).setTransact( yesno_r, causer_r ); }
+
+ /** lowlevel \c ResStatus::setLock */
+ bool setLock( const PoolItem & pi_r, bool yesno_r, Causer causer_r )
+ { return backup( pi_r ).setLock( yesno_r, causer_r ); }
+
+ /** lowlevel \c ResStatus::setTransact(true). */
+ bool setTransactTrue( const PoolItem & pi_r, Causer causer_r )
+ { return setTransact( pi_r, true, causer_r ); }
+
+ /** lowlevel \c ResStatus::setTransact(false). */
+ bool setTransactFalse( const PoolItem & pi_r, Causer causer_r )
+ { return setTransact( pi_r, false, causer_r ); }
+
+ public:
+ /** highevel set transact (force unlock). */
+ bool transact( const PoolItem & pi_r, Causer causer_r )
+ {
+ ResStatus & status( backup( pi_r ) );
+ if ( ! status.setLock( false, causer_r ) ) return false;
+ if ( ! status.setTransact( true, causer_r ) ) return false;
+ return true;
+ }
+
+ /** highlevel set locked. */
+ bool lock( const PoolItem & pi_r, Causer causer_r )
+ {
+ ResStatus & status( backup( pi_r ) );
+ if ( ! status.setTransact( false, causer_r ) ) return false;
+ if ( ! status.setLock( true, causer_r ) ) return false;
+ return true;
+ }
+
+ /** highlevel unlock (also unsets transact). */
+ bool unlock( const PoolItem & pi_r, Causer causer_r )
+ {
+ ResStatus & status( backup( pi_r ) );
+ if ( ! status.setTransact( false, causer_r ) ) return false;
+ if ( ! status.setLock( false, causer_r ) ) return false;
+ return true;
+ }
+
+ /** highlevel remove transact from non-multiversion packages. */
+ bool unsetNonMultiTransact( const PoolItem & pi_r, Causer causer_r )
+ {
+ ResStatus & status( backup( pi_r ) );
+ if ( status.transacts() && ! pi_r.multiversionInstall() )
+ {
+ if ( ! status.setTransact( false, causer_r ) ) return false;
+ }
+ return true;
+ }
+
+ /** highlevel remove transact from multiversion packages. */
+ bool unsetMultiTransact( const PoolItem & pi_r, Causer causer_r )
+ {
+ ResStatus & status( backup( pi_r ) );
+ if ( status.transacts() && pi_r.multiversionInstall() )
+ {
+ if ( ! status.setTransact( false, causer_r ) ) return false;
+ }
+ return true;
+ }
+
+ /** Highlevel action. */
+ typedef bool (StatusBackup::*Action)( const PoolItem &, Causer );
+
+ /** Highlevel action on range of items. */
+ template <class TIter>
+ bool forEach( TIter begin_r, TIter end_r, Action action_r, Causer causer_r )
+ {
+ for_( it, begin_r, end_r )
+ if ( ! (this->*action_r)( *it, causer_r ) )
+ return false;
+ return true;
+ }
+
+ private:
+ std::vector<resstatus::StatusBackup> _backup;
+ };
+
///////////////////////////////////////////////////////////////////
//
// CLASS NAME : StatusHelper
//
- /**
+ /** \todo Unify status and pickStatus.
*/
struct StatusHelper
{
- StatusHelper( const Selectable::Impl & impl )
+ StatusHelper( const Selectable::Impl & impl, ResStatus::TransactByValue causer_r )
: _impl( impl )
, inst( impl.installedObj() )
, cand( impl.candidateObj() )
+ , causer( causer_r )
{}
typedef Selectable::Impl::available_const_iterator available_const_iterator;
// Queries
//
bool hasInstalled() const
- { return inst; }
+ { return bool(inst); }
bool hasCandidate() const
- { return cand; }
+ { return bool(cand); }
bool hasInstalledOnly() const
{ return inst && !cand; }
bool hasBoth() const
{ return inst && cand; }
- //
- // ResStatus manip
- //
- void resetTransactingCandidates() const
+ /** \name Topevel methods must restore status on failure. */
+ //@{
+ bool setInstall()
{
- for ( available_const_iterator it = _impl.availableBegin();
- it != _impl.availableEnd(); ++it )
- {
- (*it).status().setTransact( false, ResStatus::USER );
+ if ( cand )
+ {
+ if ( inst ) {
+ for_( it, _impl.installedBegin(), _impl.installedEnd() )
+ {
+ ResStatus & inststatus( backup( it->status() ) );
+ if ( ! inststatus.setTransact( false, causer ) ) return restore();
+ if ( ! inststatus.setLock ( false, causer ) ) return restore();
+ if ( ! cand->multiversionInstall() )
+ {
+ // This is what the solver most probabely will do.
+ // If we are wrong the solver will correct it. But
+ // this way we will get a better disk usage result,
+ // even if no autosolving is on.
+ inststatus.setTransact( true, ResStatus::SOLVER );
+ }
+ }
}
+ if ( ! unlockCandidates() ) return restore();
+ ResStatus & candstatus( backup( cand.status() ) );
+ if ( ! candstatus.setTransact( true, causer ) ) return restore();
+ return true;
+ }
+ return false;
}
- void unlockCandidates() const
+
+ bool setDelete()
{
- for ( available_const_iterator it = _impl.availableBegin();
- it != _impl.availableEnd(); ++it )
+ if ( inst )
+ {
+ if ( ! resetTransactingCandidates() ) return restore();
+ for_( it, _impl.installedBegin(), _impl.installedEnd() )
{
- (*it).status().setTransact( false, ResStatus::USER );
- (*it).status().setLock( false, ResStatus::USER );
+ ResStatus & inststatus( backup( it->status() ) );
+ if ( ! inststatus.setLock( false, causer ) ) return restore();
+ if ( ! inststatus.setTransact( true, causer ) ) return restore();
}
+ return true;
+ }
+ return false;
}
- void lockCandidates() const
+
+ bool unset()
{
- for ( available_const_iterator it = _impl.availableBegin();
- it != _impl.availableEnd(); ++it )
+ if ( inst )
+ {
+ for_( it, _impl.installedBegin(), _impl.installedEnd() )
{
- (*it).status().setTransact( false, ResStatus::USER );
- (*it).status().setLock( true, ResStatus::USER );
+ ResStatus & inststatus( backup( it->status() ) );
+ if ( ! inststatus.setTransact( false, causer ) ) return restore();
+ if ( ! inststatus.setLock( false, causer ) ) return restore();
}
+ }
+ if ( ! unlockCandidates() ) return restore();
+ return true;
}
- bool setInstall() const
+ bool setProtected()
{
- if ( cand )
+ if ( causer != ResStatus::USER ) // by user only
+ return false;
+
+ if ( inst ) {
+ resetTransactingCandidates();
+ for_( it, _impl.installedBegin(), _impl.installedEnd() )
{
- if ( inst ) {
- inst.status().setTransact( false, ResStatus::USER );
- inst.status().setLock ( false, ResStatus::USER );
- if ( ! cand->installOnly() )
- {
- // This is what the solver most probabely will do.
- // If we are wrong the solver will correct it. But
- // this way we will get a better disk usage result,
- // even if no autosolving is on.
- inst.status().setTransact( true, ResStatus::SOLVER );
- }
- }
- unlockCandidates();
- return cand.status().setTransact( true, ResStatus::USER );
+ it->status().setTransact( false, causer );
+ it->status().setLock( true, causer );
}
- return false;
+ return true;
+ } else
+ return false;
}
- bool setDelete() const
+ bool setTaboo()
{
- if ( inst )
- {
- resetTransactingCandidates();
- inst.status().setLock( false, ResStatus::USER );
- return inst.status().setTransact( true, ResStatus::USER );
- }
- return false;
+ if ( causer != ResStatus::USER ) // by user only
+ return false;
+
+ if ( cand ) {
+ lockCandidates();
+ return true;
+ } else
+ return false;
}
+ //@}
- bool unset() const
+ private:
+ /** \name Helper methods backup status but do not replay. */
+ //@{
+ bool resetTransactingCandidates()
{
- if ( inst ) {
- inst.status().setTransact( false, ResStatus::USER );
- inst.status().setLock( false, ResStatus::USER );
- }
- unlockCandidates();
- return true;
+ for_( it, _impl.availableBegin(), _impl.availableEnd() )
+ {
+ ResStatus & status( backup( (*it).status() ) );
+ if ( ! status.setTransact( false, causer ) ) return false;
+ }
+ return true;
}
-
- bool setProtected() const
+ bool unlockCandidates()
{
- if ( inst ) {
- resetTransactingCandidates();
- inst.status().setTransact( false, ResStatus::USER );
- return inst.status().setLock( true, ResStatus::USER );
- } else
- return false;
+ for_( it, _impl.availableBegin(), _impl.availableEnd() )
+ {
+ ResStatus & status( backup( (*it).status() ) );
+ if ( ! status.setTransact( false, causer ) ) return false;
+ if ( ! status.setLock( false, causer ) ) return false;
+ }
+ return true;
}
-
- bool setTaboo() const
+ bool lockCandidates()
{
- if ( cand ) {
- lockCandidates();
- return true;
- } else
- return false;
+ for_( it, _impl.availableBegin(), _impl.availableEnd() )
+ {
+ ResStatus & status( backup( (*it).status() ) );
+ if ( ! status.setTransact( false, causer ) ) return false;
+ if ( ! status.setLock( true, causer ) ) return false;
+ }
+ return true;
}
+ //@}
-
- public:
+ private:
const Selectable::Impl & _impl;
- PoolItem inst;
- PoolItem cand;
+ PoolItem inst;
+ PoolItem cand;
+ ResStatus::TransactByValue causer;
+
+ private:
+ bool restore() { return backup.restore(); }
+ StatusBackup backup;
};
///////////////////////////////////////////////////////////////////
return( installedObj().status().isByUser() ? S_Del : S_AutoDel );
}
- if ( installedObj() && installedObj().status().isLocked() )
+ if ( installedObj() && allInstalledLocked() )
return S_Protected;
if ( !installedObj() && allCandidatesLocked() )
return S_NoInst;
}
- bool Selectable::Impl::setStatus( const Status state_r )
+ bool Selectable::Impl::setStatus( Status state_r, ResStatus::TransactByValue causer_r )
{
- StatusHelper self( *this );
+ StatusHelper self( *this, causer_r );
switch ( state_r )
{
return false;
}
- PoolItem Selectable::Impl::setCandidate( ResObject::constPtr byUser_r )
+ PoolItem Selectable::Impl::setCandidate( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r )
{
- _candidate = PoolItem();
+ PoolItem newCandidate;
- if ( byUser_r ) // must be in available list
+ if ( newCandidate_r ) // must be in available list
+ {
+ for_( it, availableBegin(), availableEnd() )
{
- for ( available_const_iterator it = availableBegin();
- it != availableEnd(); ++it )
- {
- if ( it->resolvable() == byUser_r )
- {
- _candidate = *it;
- break;
- }
- }
+ if ( *it == newCandidate_r )
+ {
+ newCandidate = *it;
+ break;
+ }
}
+ }
- if ( _candidate )
+ if ( newCandidate )
+ {
+ PoolItem trans( transactingCandidate() );
+ if ( trans && trans != newCandidate )
{
- PoolItem trans( transactingCandidate() );
- if ( trans && trans != _candidate )
- {
- // adjust transact to the new cancidate
- trans.status().setTransact( false, ResStatus::USER );
- _candidate.status().setTransact( true, ResStatus::USER );
- }
+ // adjust transact to the new cancidate
+ if ( trans.status().maySetTransact( false, causer_r )
+ && newCandidate.status().maySetTransact( true, causer_r ) )
+ {
+ trans.status().setTransact( false, causer_r );
+ newCandidate.status().setTransact( true, causer_r );
+ }
+ else
+ {
+ // No permission to change a transacting candidate.
+ // Leave _candidate untouched and return NULL.
+ return PoolItem();
+ }
}
+ }
- return _candidate;
+ return _candidate = newCandidate;
}
+ ///////////////////////////////////////////////////////////////////
+
+ bool Selectable::Impl::pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
+ {
+ if ( identicalInstalled( pi_r ) )
+ return setPickStatus( pi_r, ( yesno_r ? S_Update : S_KeepInstalled ), causer_r );
+ return setPickStatus( pi_r, ( yesno_r ? S_Install : S_NoInst ), causer_r );
+ }
+
+ bool Selectable::Impl::pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
+ {
+ return setPickStatus( pi_r, ( yesno_r ? S_Del : S_KeepInstalled ), causer_r );
+ }
+
+ bool Selectable::Impl::setPickStatus( const PoolItem & pi_r, Status state_r, ResStatus::TransactByValue causer_r )
+ {
+ if ( pi_r.ident() != ident() )
+ return false; // not my PoolItem
+
+ StatusBackup backup;
+ std::vector<PoolItem> i;
+ std::vector<PoolItem> a;
+
+ for_( it, installedBegin(), installedEnd() )
+ if ( identical( *it, pi_r ) )
+ i.push_back( *it );
+ for_( it, availableBegin(), availableEnd() )
+ if ( identical( *it, pi_r ) )
+ a.push_back( *it );
+
+ switch ( state_r )
+ {
+ case S_Protected:
+ if ( causer_r == ResStatus::USER && ! i.empty() )
+ {
+ if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::lock, causer_r ) ) return backup.restore();
+ if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::setTransactFalse, causer_r ) ) return backup.restore();
+ return true;
+ }
+ break;
+
+ case S_Taboo:
+ if ( causer_r == ResStatus::USER && ! a.empty() )
+ {
+ if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::lock, causer_r ) ) return backup.restore();
+ return true;
+ }
+ break;
+
+ case S_AutoDel:
+ case S_AutoInstall:
+ case S_AutoUpdate:
+ // Auto level is SOLVER level. UI may query, but not
+ // set at this level.
+ break;
+
+ case S_Del:
+ if ( ! i.empty() )
+ {
+ if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::transact, causer_r ) ) return backup.restore();
+ if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::setTransactFalse, causer_r ) ) return backup.restore();
+ return true;
+ }
+ break;
+
+ case S_Install:
+ if ( i.empty() && ! a.empty() )
+ {
+ const PoolItem & cand( pi_r.status().isInstalled() ? *a.begin() : pi_r );
+ if ( cand.multiversionInstall() )
+ {
+ if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetNonMultiTransact, causer_r ) ) return backup.restore();
+ // maybe unlock candidate only?
+ if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+ if ( ! cand.status().setTransact( true, causer_r ) ) return backup.restore();
+ return true;
+ }
+ else
+ {
+ // For non-multiversion use ordinary setStatus
+ // NOTE that S_Update/S_Install here depends on !installedEmpty()
+ // and not on picklists identicalInstalled.
+ if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetMultiTransact, causer_r ) ) return backup.restore();
+ if ( ! setCandidate( cand, causer_r ) ) return backup.restore();
+ if ( ! setStatus( installedEmpty() ? S_Install : S_Update, causer_r ) ) return backup.restore();
+ return true;
+ }
+ }
+ break;
+
+ case S_Update:
+ if ( ! i.empty() && ! a.empty() )
+ {
+ const PoolItem & cand( pi_r.status().isInstalled() ? *a.begin() : pi_r );
+ if ( cand.multiversionInstall() )
+ {
+ if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+ if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::setTransactTrue, ResStatus::SOLVER ) ) return backup.restore();
+ if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetNonMultiTransact, causer_r ) ) return backup.restore();
+ // maybe unlock candidate only?
+ if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+ if ( ! cand.status().setTransact( true, causer_r ) ) return backup.restore();
+ return true;
+ }
+ else
+ {
+ // For non-multiversion use ordinary setStatus
+ // NOTE that S_Update/S_Install here depends on !installedEmpty()
+ // and not on picklists identicalInstalled.
+ if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetMultiTransact, causer_r ) ) return backup.restore();
+ if ( ! setCandidate( cand, causer_r ) ) return backup.restore();
+ if ( ! setStatus( installedEmpty() ? S_Install : S_Update, causer_r ) ) return backup.restore();
+ return true;
+ }
+ }
+ break;
+
+ case S_KeepInstalled:
+ if ( ! i.empty() )
+ {
+ if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+ if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+ return true;
+ }
+ break;
+
+ case S_NoInst:
+ if ( i.empty() )
+ {
+ if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ Status Selectable::Impl::pickStatus( const PoolItem & pi_r ) const
+ {
+ if ( pi_r.satSolvable().ident() != ident() )
+ return Status(-1); // not my PoolItem
+
+ std::vector<PoolItem> i;
+ std::vector<PoolItem> a;
+ PoolItem ti;
+ PoolItem ta;
+
+ for_( it, installedBegin(), installedEnd() )
+ if ( identical( *it, pi_r ) )
+ {
+ i.push_back( *it );
+ if ( ! ti && it->status().transacts() )
+ ti = *it;
+ }
+
+ for_( it, availableBegin(), availableEnd() )
+ if ( identical( *it, pi_r ) )
+ {
+ a.push_back( *it );
+ if ( ! ta && it->status().transacts() )
+ ta = *it;
+ }
+
+ if ( ta )
+ {
+ if ( ta.status().isByUser() )
+ return( i.empty() ? S_Install : S_Update );
+ else
+ return( i.empty() ? S_AutoInstall : S_AutoUpdate );
+ }
+
+ if ( ti )
+ {
+ return( ti.status().isByUser() ? S_Del : S_AutoDel );
+ }
+
+ for_( it, i.begin(), i.end() )
+ if ( it->status().isLocked() )
+ return S_Protected;
+
+ if ( i.empty() )
+ {
+ bool allALocked = true;
+ for_( it, a.begin(), a.end() )
+ if ( ! it->status().isLocked() )
+ {
+ allALocked = false;
+ break;
+ }
+ if ( allALocked )
+ return S_Taboo;
+ }
+
+ // KEEP state:
+ if ( ! i.empty() )
+ return S_KeepInstalled;
+ // Report pseudo installed items as installed, if they are satisfied.
+ if ( traits::isPseudoInstalled( kind() )
+ && ( ta ? ta : *a.begin() ).status().isSatisfied() ) // no installed, so we must have candidate
+ return S_KeepInstalled;
+
+ return S_NoInst;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+
ResStatus::TransactByValue Selectable::Impl::modifiedBy() const
{
PoolItem cand( candidateObj() );