// -*- Mode: C++; c-basic-offset: 4; -*- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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() ), 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 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 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(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( "" ) + */ v.package().name( std::string( "oops" ) ) /* + "" */ ); TQString l = u8( v.longDescription( u8( i18n( "No long description available" ) ) ) ); m_description->setText( TQString( "" ) + formatLongDescription( l ) + "" ); 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 = "" + what + " " + (nobr ? "" : "") + txt + (nobr ? "" : "") + "
"; return ret; } void ListerTooltip::maybeTip( const TQPoint &pt ) { if ( !m_parent ) return; kdDebug() << "ListTreeWidgetTooltip::maybeTip ()" << endl; ListerItem *x = dynamic_cast( m_parent->itemAt( pt ) ); if ( !x ) return; if ( x->extender() ) return; // no tips for extended items, thank you TQString str = u8( "" ); TQString descr, cand, cur; descr = cand = cur = i18n( "Not available" ); entity::Package p( x->entity() ); descr = p.shortDescription( std::string( i18n( "Not available" ).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 Version:" ), cur ); str += format( i18n( "Candidate Version:" ), cand ); str.append( u8( "" ) ); tip( m_parent->itemRect( x ), str ); }