You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
adept/adept/libadept/lister.cpp

708 lines
21 KiB

// -*- Mode: C++; c-basic-offset: 4; -*-
#include <tqlabel.h>
#include <tqtimer.h>
#include <tqpainter.h>
#include <tqpushbutton.h>
#include <tqthread.h>
#include <tqtextbrowser.h>
#include <tdepopupmenu.h>
#include <kdebug.h>
#include <klineedit.h>
#include <tdelocale.h>
#include <tdeapplication.h>
#include <tdeglobal.h>
#include <kiconloader.h>
#include <functional>
#include <iostream>
#include <apt-front/cache/cache.h>
#include <apt-front/actor.h>
#include <apt-front/cache/component/packages.h>
#include <apt-front/cache/component/state.h>
#include <apt-front/predicate/factory.h>
#include <adept/utils.h>
#include <adept/lister.h>
#include <adept/packageinfo.h>
using namespace aptFront;
using namespace aptFront::predicate;
using namespace aptFront::cache;
using namespace aptFront::utils;
using namespace adept;
Lister::Lister( TQWidget *parent, const char *name )
: ExtendableList( parent, name ),
m_rangeProvider( 0 ),
m_baseF( predicate::True< entity::Entity >() ),
m_interactiveF( True<entity::Entity>() ), m_itemCount( -1 ),
m_rebuildScheduled( false ), m_inRebuild( false ), m_cancelRebuild( false ),
m_openToplevel( false ), m_rebuildMutex( true )
{
observeComponent< component::State >();
observeComponent< component::Packages >();
observeComponent< component::PackageTags >();
setRootIsDecorated( false );
setSelectionModeExt( Extended );
setAllColumnsShowFocus (true);
m_icons[ u8( "package-install" ) ] = u8( "adept_install" );
m_icons[ u8( "package-remove" ) ] = u8( "adept_remove" );
m_icons[ u8( "package-upgrade" ) ] = u8( "adept_upgrade" );
m_icons[ u8( "package-keep" )] = u8( "adept_keep" );
m_icons[ u8( "package-reinstall" )] = u8( "adept_reinstall" );
m_icons[ u8( "package-purge" )] = u8( "adept_purge" );
setSorting( -1 );
// addColumn(" ", 40);
// addColumn(" ", 18);
addColumn( i18n( "Package" ), 180);
addColumn( i18n( "Status" ), 110);
addColumn( i18n( "Requested" ), 90);
addColumn( i18n( "Description" ), 300);
setToggleColumn( 0 );
setResizeMode (LastColumn);
connect( this, TQT_SIGNAL( selectionChanged() ), TQT_SLOT( updateActions() ) );
connect( this,
TQT_SIGNAL( contextMenuRequested( TQListViewItem *,
const TQPoint &, int ) ),
this, TQT_SLOT(
contextMenu( TQListViewItem *, const TQPoint &, int) ) );
m_tip = 0;
// m_tip = new ListerTooltip( viewport(), this );
}
Lister::~Lister()
{
delete m_tip;
}
void Lister::scheduleRebuild()
{
if (!m_rebuildScheduled) {
// kdDebug() << "Lister scheduling rebuild" << endl;
TQTimer::singleShot( 0, this, TQT_SLOT( rebuild() ) );
}
m_rebuildScheduled = true;
}
void Lister::updateActions()
{
emit actionsChanged( this );
}
void Lister::notifyPostChange( component::Base * )
{
kdDebug() << "notifyRefresh()" << endl;
updateActions();
triggerUpdate();
}
void Lister::notifyPreRebuild( component::Base *b )
{
kdDebug() << "Lister::notifyPreRebuild( " << b << " )" << endl;
Cache &c = cache::Global::get( m_cache );
setEnabled( false );
if ( dynamic_cast< component::PackageTags * >( b ) != 0 ) {
kdDebug() << "clearing cardinality" << endl;
m_cardinality.clear();
}
if ( dynamic_cast< component::Packages * >( b ) != 0 ) {
kdDebug() << "clearing lister" << endl;
clear();
m_items.clear();
m_cardinality.clear();
}
}
void Lister::notifyPostRebuild( component::Base *b )
{
kdDebug() << "Lister::notifyPostRebuild( " << b << " )" << endl;
scheduleRebuild();
if ( dynamic_cast< component::State * >( b ) != 0 ) {
setEnabled( true );
}
}
bool lessByName( const entity::Entity &e1, const entity::Entity &e2 )
{
if ( e1.is< entity::Named >() && e2.is< entity::Named >() ) {
entity::Named &n1 = downcast< entity::Named >( e1 ),
&n2 = downcast< entity::Named >( e2 );
return n1.name() < n2.name();
}
return e1 < e2;
}
bool Lister::cancelRebuild() {
// kdDebug() << "cancel rebuild: " << m_inRebuild << ", " << m_cancelRebuild << endl;
if ( m_inRebuild ) {
m_rebuildScheduled = false;
m_cancelRebuild = true;
}
if ( !cache::Global::get( m_cache ).isOpen() ) {
m_rebuildScheduled = false;
return true;
}
return m_cancelRebuild;
}
void Lister::cleanRebuild()
{
scheduleRebuild();
}
Lister::CreateItem::CreateItem( Lister *_l, ListerItem *p )
: l( _l ), time( 0 ), items( 0 ), last( 0 ), parent( p )
{
}
Lister::CreateItem::~CreateItem()
{
// delete timer;
}
Lister::Map::value_type Lister::CreateItem::operator()( entity::Entity e )
{
items ++;
if ( l->m_cancelRebuild ) throw 0; // XXX proper exception please
// kdDebug() << "trying to acquire mutex" << endl;
l->m_rebuildMutex.lock();
// kdDebug() << "mutex acquired" << endl;
// count tags
if ( e.is< entity::Package >() ) {
const entity::Tag::Set &tags = downcast< entity::Package >( e ).tags();
for (entity::Tag::Set::iterator i = tags.begin(); i != tags.end(); ++ i )
l->m_cardinality[ *i ] ++;
}
if ( last ) {
if ( parent )
last = new ListerItem( parent, last, e );
else
last = new ListerItem( l, last, e );
} else {
if ( parent )
last = new ListerItem( parent, e );
else
last = new ListerItem( l, e );
}
l->m_rebuildMutex.unlock();
if ( e.is< entity::Relation >() )
// this should be safe because the parent thread is waiting
// for us and ensures that universe (libapt-front) is kept
// unchanged while we run
l->insertRangeInternal( InsertRangePair(
last, downcast< entity::Relation >( e ).targetPackages() ) );
// we may want to use recursive async call instead? why?
/* Threads::enqueue(
asyncCall( std::bind2nd( std::mem_fun( &Lister::insertRangeInternal ),
InsertRangePair(
last,
downcast< entity::Relation >( e ).targetPackages() ) ),
l ), &(l->m_rebuildMutex) );
*/
return std::make_pair( e, last );
}
void Lister::reallyUpdate()
{
bool en = isUpdatesEnabled();
setUpdatesEnabled( true );
triggerUpdate();
setUpdatesEnabled( en );
}
void Lister::insertRange( Range r ) {
insertRangeInternal( InsertRangePair( 0, r ) );
}
void Lister::insertRangeInternal( InsertRangePair a )
{
// kdDebug() << "insertRange running..." << endl;
try {
std::transform( a.second, a.second.end(),
inserter( m_items, m_items.begin() ),
CreateItem( this, a.first ) );
} catch ( ... ) {}
m_itemCount = m_items.size();
}
/* void Lister::rebuildInsertRange( Range r ) {
insertRange( 0, r );
} */
void Lister::rebuild()
{
Cache &c = cache::Global::get( m_cache );
if ( cancelRebuild() ) {
scheduleRebuild();
return;
}
m_inRebuild = true;
m_rebuildMutex.lock();
emit rebuildStarted();
c.progress().OverallProgress( 0, 0, 0, i18n( "Filtering" ) );
kdDebug() << "rebuild running" << endl;
clock_t _c = clock(), C;
for ( Cardinality::iterator i = m_cardinality.begin();
i != m_cardinality.end(); ++i )
i->second = 0;
kdDebug() << "querying m_rangeProvider " << m_rangeProvider << "..." << endl;
Range r = filteredRange( m_rangeProvider ?
m_rangeProvider->listerRange() : range( VectorRange() ),
m_baseF );
C = (clock() - _c) / 1000; _c = clock();
setUpdatesEnabled( false );
kdDebug() << "clearing..." << endl;
clear();
m_items.clear();
kdDebug() << "asyncCall to rebuildInsertRange..." << endl;
TQThread *t = asyncCall( std::bind2nd(
std::mem_fun( &Lister::insertRangeInternal ),
InsertRangePair( 0, r ) ),
this );
kdDebug() << "starting the thread..." << endl;
TQTimer timer;
connect( &timer, TQT_SIGNAL( timeout() ),
this, TQT_SLOT( reallyUpdate() ) );
timer.start( 0 );
m_rebuildMutex.unlock();
Threads::enqueue( t, &m_rebuildMutex );
Threads::wait();
timer.stop();
kdDebug() << "thread finished..." << endl;
C = (clock() - _c) / 1000; _c = clock();
kdDebug() << m_items.size() << " entities synced, time = " << C << endl;
setUpdatesEnabled( true );
c.progress().Done();
if ( m_openToplevel ) openToplevel();
triggerUpdate();
if ( !m_cancelRebuild ) {
for ( Cardinality::iterator i = m_cardinality.begin();
i != m_cardinality.end(); ++i )
if ( i->second == m_itemCount )
i->second = -1;
emit cardinalityChanged( m_cardinality );
}
m_inRebuild = false;
m_rebuildScheduled = false;
m_cancelRebuild = false;
emit rebuildFinished();
}
void Lister::baseAnd( Predicate o )
{
m_baseF = predicate::predicate( m_baseF and o );
// emit filterChanged( m_baseF );
cancelRebuild();
scheduleRebuild();
}
void Lister::baseSet( Predicate o )
{
m_baseF = o;
// emit filterChanged( m_baseF );
cancelRebuild();
scheduleRebuild();
}
void Lister::interactiveAnd( Predicate o )
{
m_interactiveF = predicate::predicate( m_interactiveF and o );
cancelRebuild();
scheduleRebuild();
}
void Lister::interactiveDrop( Predicate o )
{
m_interactiveF = predicate::remove( m_interactiveF, o );
cancelRebuild();
scheduleRebuild();
}
bool Lister::itemSelected( Map::value_type i )
{
return not i.second->isSelected();
}
entity::Entity Lister::extractKey( Map::value_type i )
{
return i.first;
}
Lister::VectorRange Lister::selection()
{
VectorRange ret;
Map m;
std::remove_copy_if( m_items.begin(), m_items.end(),
inserter( m, m.begin() ),
itemSelected );
std::transform( m.begin(), m.end(),
consumer( ret ),
extractKey );
return ret;
}
Lister::VectorRange Lister::content()
{
VectorRange ret;
std::transform( m_items.begin(), m_items.end(),
consumer( ret ),
extractKey );
return ret;
}
TQString ListerItem::text( int column ) const
{
// if (column == 0) return ""; // until we redo paintcell for the col
if (entity().is<entity::Package>()) {
entity::Package p = entity();
switch (column) {
case 0: return u8( p.name( u8( i18n( "n/a" ) ) ) );
case 1: return u8( p.statusString( u8( i18n( "n/a" ) ) ) );
case 2: return u8( p.actionString( u8( i18n( "n/a" ) ) ) );
case 3: return u8( p.shortDescription( u8( i18n( "n/a" ) ) ) );
// case 2: return p.candidateVersion().versionString();
}
}
if ( entity().is< entity::Relation >() && column == 0 )
return downcast< entity::Relation >( entity() ).format();
return u8( "" );
}
void ListerItem::paintCell (TQPainter *p, const TQColorGroup &cg,
int column, int width, int alignment )
{
if ( width <= 0 )
return;
TQColorGroup _cg( cg );
TQColor c = _cg.text();
TQPixmap pm( width, height() );
TQPainter _p( &pm );
if (entity().is<entity::Package>()) {
entity::Package p = entity();
if (column == 1)
c = statusColor( p );
if (column == 2)
c = actionColor( p );
}
_cg.setColor( TQColorGroup::Text, c );
TDEListViewItem::paintCell( &_p, _cg, column, width, AlignTop );
p->drawPixmap( 0, 0, pm );
}
void Lister::contextMenu( TQListViewItem *it, const TQPoint &pt, int /*c*/ )
{
if (! it) // check for actor when we have one...
return;
m_context = dynamic_cast< ListerItem * >( it );
VectorRange sel = selection();
// entity::Package p = (dynamic_cast<ListerItem *>(it)->entity());
TDEPopupMenu *m = new TDEPopupMenu (this);
utils::Range< Actor > r = actor::Global< entity::Package >::list();
int id = 8;
try {
for (; r != r.end(); ++r) {
m->insertItem( SmallIconSet( m_icons[ u8( r->name() ) ] ),
r->prettyName(), id );
m->setItemEnabled(
id, r->possible( utils::upcastRange< entity::Package >( sel ) ) );
++id;
}
} catch ( std::bad_cast ) {} // ignore (this is broken, but
// easiest fix)
bool open = m_context->extender();
m->insertItem( open ? i18n( "Hide details" ) :
i18n( "Show details" ), open ? 1 : 0 );
connect(m, TQT_SIGNAL(activated(int)), this, TQT_SLOT(contextActivated(int)));
m->exec(pt);
delete m;
}
void Lister::contextActivated( int id )
{
VectorRange sel = selection();
try {
if (id >= 8) {
utils::Range< Actor > r = actor::Global< entity::Package >::list();
std::advance( r, id - 8 );
(*r)( utils::upcastRange< entity::Package >( sel ) );
updateActions();
}
if (id < 8) {
VectorRange i = sel.begin();
while (i != i.end()) {
if (id == 0)
m_items[*i]->showExtender();
if (id == 1)
m_items[*i]->hideExtender();
++ i;
}
}
} catch ( std::bad_cast ) {} // ignore (this is broken, but
}
ListerItemExtender::~ListerItemExtender()
{
}
ListerItemExtender::ListerItemExtender( TQWidget *parent, const char * n)
: ListerItemExtenderUi( parent, n )
{
observeComponent< component::State >();
adjustFontSize( m_description, -1 );
connect( m_details, TQT_SIGNAL( clicked() ),
this, TQT_SLOT( detailsClicked() ) );
m_packageInfo->adjustFontSize( -1 );
m_packageInfo->hideStatus();
}
void ListerItemExtender::detailsClicked() {
detailsRequested( m_entity );
}
ListerItem *ListerItemExtender::item()
{
return dynamic_cast< ListerItem * >( m_item );
}
void ListerItemExtender::mouseReleaseEvent( TQMouseEvent *e ) {
e->ignore();
if ( childAt( e->pos() ) != static_cast< TQWidget * >( m_name ) )
e->accept();
}
void ListerItemExtender::setItem( ExtendableItem *i )
{
ItemExtender::setItem( i );
m_entity = item()->entity();
// setupColors();
kdDebug() << "ListerItemExtender::setItem connecting" << endl;
connect( this, TQT_SIGNAL( detailsRequested( Lister::Entity ) ),
item()->list(), TQT_SIGNAL( detailsRequested( Lister::Entity ) ) );
entity::Version v;
entity::Package p;
if ( m_entity.is< entity::Version >() ) {
v = m_entity;
p = v.package();
}
if ( m_entity.is< entity::Package >() ) {
p = m_entity;
v = p.anyVersion();
}
if ( !v.valid() ) {
m_logical->setText( i18n( "Immutable" ) );
m_logical->setEnabled( false );
m_details->setEnabled( false );
return;
}
m_name->setText( /* TQString( "<b>" ) + */
v.package().name( std::string( "oops" ) ) /* + "</b>" */ );
TQString l = u8( v.longDescription(
u8( i18n( "No long description available" ) ) ) );
m_description->setText( TQString( "<qt>" )
+ formatLongDescription( l ) + "</qt>" );
m_description->adjustSize();
m_description->installEventFilter( this );
m_packageInfo->setVersion( v, m_entity.is< entity::Version >() );
notifyPostChange( 0 );
}
void ListerItemExtender::notifyPostRebuild( component::Base *b )
{ // need to catch undo/redo effects
return notifyPostChange( b );
}
void ListerItemExtender::notifyPostChange( component::Base * )
{
// without the timer to break it, there could be a loop where
// we connect the clicked() signal to a slot which would be
// invoked right away when we return -> evil
TQTimer::singleShot( 0, this, TQT_SLOT( updateLogical() ) );
}
void ListerItemExtender::updateLogical() {
entity::Package pkg = entity();
EntityActor *a = 0;
m_status->setText( colorify(
statusColor( pkg ),
u8( pkg.statusString( u8( i18n( "Unknown" ) ) ) ) ) );
m_change->setText( colorify(
actionColor( pkg ),
u8( pkg.actionString( u8( i18n( "Unknown" ) ) ) ) ) );
m_logical->setEnabled( true );
if (pkg.canUpgrade()) {
a = new EntityActor( pkg.upgrade() );
} else if (pkg.canInstall()) {
a = new EntityActor( pkg.install() );
} else if (pkg.canKeep()) {
a = new EntityActor( pkg.keep() );
} else if (pkg.canRemove()) {
a = new EntityActor( pkg.remove() );
}
if (a) {
m_logical->setText( u8( a->actor().prettyName() ) );
connect( m_logical, TQT_SIGNAL( clicked() ),
a, TQT_SLOT( destructiveAct() ) );
} else {
m_logical->setText( i18n( "Immutable" ) );
m_logical->setEnabled( false );
}
}
bool ListerItemExtender::eventFilter( TQObject *o, TQEvent *e )
{
if (o == m_description && e->type() == TQEvent::Wheel) {
// kdDebug() << "discarding wheel event..." << endl;
TQApplication::sendEvent( this, e );
return true;
}
return false;
}
void ListerItemExtender::resize( int w, int h )
{
int namew = - item()->lister()->extenderOffset( item() ) - 2
- layout()->margin()
- layout()->spacing()
+ item()->lister()->columnWidth( 0 );
int statw = item()->lister()->columnWidth( 1 )
- layout()->spacing();
int chw = item()->lister()->columnWidth( 2 ) - 2
- layout()->spacing();
m_name->setMinimumWidth( namew );
m_status->setMinimumWidth( statw );
m_change->setMinimumWidth( chw );
m_packageInfo->adjustSize();
m_leftHeight = m_name->height() + m_packageInfo->height()
+ m_logical->height() + 20;
TQWidget::resize( w, 500 );
TQWidget::resize(
w,
TQMAX( m_description->contentsHeight() + 16,
m_leftHeight ) );
}
bool entityLess::operator()( entity::Entity e1, entity::Entity e2 )
{
if ( e1.is< entity::Package >() ) {
if ( e2.is< entity::Package >() )
return e1 < e2;
return true;
}
if ( e1.is< entity::Version >() ) {
if ( e2.is< entity::Package >() )
return false;
if ( e2.is< entity::Version >() )
return e1 < e2;
return true;
}
if ( e1.is< entity::Relation >() ) {
if ( e2.is< entity::Package >() )
return false;
if ( e2.is< entity::Version >() )
return false;
if ( e2.is< entity::Relation >() )
return e1 < e2;
return true;
}
return true;
}
bool ListerItem::less( const ExtendableItem *b ) const
{
entity::Entity e1 = entity(), e2 = dynamic_cast< const ListerItem * >( b )->entity();
return entityLess()( e1, e2 );
}
bool ListerItem::keepLess( const ListerItem *o ) const
{
const ListerItem *b = o;
while ( b != 0 ) {
if ( b == this )
return false;
b = b->m_previous;
}
while ( o != 0 ) {
o = dynamic_cast< const ListerItem * >( o->nextSibling() );
if ( o == this )
return true;
}
return false;
}
TQString ListerTooltip::format( const TQString &what,
const TQString &txt, bool nobr )
{
TQString ret = "<b>" + what + "</b>&nbsp;" + (nobr ? "<nobr>" : "")
+ txt + (nobr ? "</nobr>" : "") + "<br>";
return ret;
}
void ListerTooltip::maybeTip( const TQPoint &pt )
{
if ( !m_parent )
return;
kdDebug() << "ListTreeWidgetTooltip::maybeTip ()" << endl;
ListerItem *x = dynamic_cast<ListerItem *>( m_parent->itemAt( pt ) );
if ( !x )
return;
if ( x->extender() )
return; // no tips for extended items, thank you
TQString str = u8( "<qt>" );
TQString descr, cand, cur;
descr = cand = cur = i18n( "<i>Not available</i>" );
entity::Package p( x->entity() );
descr = p.shortDescription( std::string(
i18n( "<i>Not available</i>" ).local8Bit() ) );
try {
cand = u8( p.candidateVersion().versionString() );
} catch (...) {}
try {
cur = u8( p.installedVersion().versionString() );
} catch (...) {}
str += format( i18n( "Package:" ), u8( p.name() ) );
str += format( i18n( "Description:" ), descr );
str += format( i18n( "Current&nbsp;Version:" ), cur );
str += format( i18n( "Candidate&nbsp;Version:" ), cand );
str.append( u8( "</qt>" ) );
tip( m_parent->itemRect( x ), str );
}