/* KNode, the KDE newsreader Copyright (c) 1999-2005 the KNode authors. See file AUTHORS for details This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US */ #include <tqcursor.h> #include <tqheader.h> #include <tqstylesheet.h> #include <tqtimer.h> #include <klocale.h> #include <kdebug.h> #include <tdeversion.h> #include <kpopupmenu.h> #include "knglobals.h" #include "knconfigmanager.h" #include "headerview.h" #include "knhdrviewitem.h" #include "kngroupmanager.h" #include "knarticle.h" #include "knarticlemanager.h" #include "knmainwidget.h" KNHeaderView::KNHeaderView(TQWidget *parent, const char *name) : KListView(parent,name), mSortCol( -1 ), mSortAsc( true ), mSortByThreadChangeDate( false ), mDelayedCenter( -1 ), mActiveItem( 0 ), mShowingFolder( false ), mInitDone( false ) { mPaintInfo.subCol = addColumn( i18n("Subject"), 310 ); mPaintInfo.senderCol = addColumn( i18n("From"), 115 ); mPaintInfo.scoreCol = addColumn( i18n("Score"), 42 ); mPaintInfo.sizeCol = addColumn( i18n("Lines"), 42 ); mPaintInfo.dateCol = addColumn( i18n("Date"), 102 ); setDropVisualizer( false ); setDropHighlighter( false ); setItemsRenameable( false ); setItemsMovable( false ); setAcceptDrops( false ); setDragEnabled( true ); setAllColumnsShowFocus( true ); setSelectionMode( TQListView::Extended ); setShowSortIndicator( true ); setShadeSortColumn ( true ); setRootIsDecorated( true ); setSorting( mPaintInfo.dateCol ); header()->setMovingEnabled( true ); setColumnAlignment( mPaintInfo.sizeCol, TQt::AlignRight ); setColumnAlignment( mPaintInfo.scoreCol, TQt::AlignRight ); // due to our own column text squeezing we need to repaint on column resizing disconnect( header(), TQT_SIGNAL(sizeChange(int, int, int)) ); connect( header(), TQT_SIGNAL(sizeChange(int, int, int)), TQT_SLOT(slotSizeChanged(int, int, int)) ); // column selection RMB menu mPopup = new KPopupMenu( this ); mPopup->insertTitle( i18n("View Columns") ); mPopup->setCheckable( true ); mPopup->insertItem( i18n("Line Count"), KPaintInfo::COL_SIZE ); mPopup->insertItem( i18n("Score"), KPaintInfo::COL_SCORE ); connect( mPopup, TQT_SIGNAL(activated(int)), this, TQT_SLOT(toggleColumn(int)) ); // connect to the article manager connect( knGlobals.articleManager(), TQT_SIGNAL(aboutToShowGroup()), TQT_SLOT(prepareForGroup()) ); connect( knGlobals.articleManager(), TQT_SIGNAL(aboutToShowFolder()), TQT_SLOT(prepareForFolder()) ); new KNHeaderViewToolTip( this ); installEventFilter( this ); } KNHeaderView::~KNHeaderView() { // ### crash because KNConfigManager is already deleted here // writeConfig(); } void KNHeaderView::readConfig() { if ( !mInitDone ) { TDEConfig *conf = knGlobals.config(); conf->setGroup( "HeaderView" ); mSortByThreadChangeDate = conf->readBoolEntry( "sortByThreadChangeDate", false ); restoreLayout( conf, "HeaderView" ); mInitDone = true; } KNConfig::ReadNewsGeneral *rngConf = knGlobals.configManager()->readNewsGeneral(); toggleColumn( KPaintInfo::COL_SIZE, rngConf->showLines() ); if ( !mShowingFolder ) // score column is always hidden when showing a folder toggleColumn( KPaintInfo::COL_SCORE, rngConf->showScore() ); mDateFormatter.setCustomFormat( rngConf->dateCustomFormat() ); mDateFormatter.setFormat( rngConf->dateFormat() ); KNConfig::Appearance *app = knGlobals.configManager()->appearance(); TQPalette p = palette(); p.setColor( TQColorGroup::Base, app->backgroundColor() ); p.setColor( TQColorGroup::Text, app->textColor() ); setPalette( p ); setAlternateBackground( app->alternateBackgroundColor() ); setFont( app->articleListFont() ); } void KNHeaderView::writeConfig() { TDEConfig *conf = knGlobals.config(); conf->setGroup( "HeaderView" ); conf->writeEntry( "sortByThreadChangeDate", mSortByThreadChangeDate ); saveLayout( conf, "HeaderView" ); KNConfig::ReadNewsGeneral *rngConf = knGlobals.configManager()->readNewsGeneral(); rngConf->setShowLines( mPaintInfo.showSize ); if ( !mShowingFolder ) // score column is always hidden when showing a folder rngConf->setShowScore( mPaintInfo.showScore ); } void KNHeaderView::setActive( TQListViewItem *i ) { KNHdrViewItem *item = static_cast<KNHdrViewItem*>( i ); if ( !item || item->isActive() ) return; if ( mActiveItem ) { mActiveItem->setActive( false ); repaintItem( mActiveItem ); mActiveItem = 0; } item->setActive( true ); setSelected( item, true ); setCurrentItem( i ); ensureItemVisibleWithMargin( i ); mActiveItem = item; emit( itemSelected(item) ); } void KNHeaderView::clear() { mActiveItem = 0; TQListView::clear(); } void KNHeaderView::ensureItemVisibleWithMargin( const TQListViewItem *i ) { if ( !i ) return; TQListViewItem *parent = i->parent(); while ( parent ) { if ( !parent->isOpen() ) parent->setOpen( true ); parent = parent->parent(); } mDelayedCenter = -1; int y = itemPos( i ); int h = i->height(); if ( knGlobals.configManager()->readNewsGeneral()->smartScrolling() && ((y + h + 5) >= (contentsY() + visibleHeight()) || (y - 5 < contentsY())) ) { ensureVisible( contentsX(), y + h/2, 0, h/2 ); mDelayedCenter = y + h/2; TQTimer::singleShot( 300, this, TQT_SLOT(slotCenterDelayed()) ); } else { ensureVisible( contentsX(), y + h/2, 0, h/2 ); } } void KNHeaderView::slotCenterDelayed() { if ( mDelayedCenter != -1 ) ensureVisible( contentsX(), mDelayedCenter, 0, visibleHeight() / 2 ); } void KNHeaderView::setSorting( int column, bool ascending ) { if ( column == mSortCol ) { mSortAsc = ascending; if ( mInitDone && column == mPaintInfo.dateCol && ascending ) mSortByThreadChangeDate = !mSortByThreadChangeDate; } else { mSortCol = column; emit sortingChanged( column ); } KListView::setSorting( column, ascending ); if ( currentItem() ) ensureItemVisible( currentItem() ); if ( mSortByThreadChangeDate ) setColumnText( mPaintInfo.dateCol , i18n("Date (thread changed)") ); else setColumnText( mPaintInfo.dateCol, i18n("Date") ); } void KNHeaderView::nextArticle() { KNHdrViewItem *it = static_cast<KNHdrViewItem*>( currentItem() ); if (it) { if (it->isActive()) { // take current article, if not selected if (it->isExpandable()) it->setOpen(true); it = static_cast<KNHdrViewItem*>(it->itemBelow()); } } else it = static_cast<KNHdrViewItem*>( firstChild() ); if(it) { clearSelection(); setActive( it ); setSelectionAnchor( currentItem() ); } } void KNHeaderView::prevArticle() { KNHdrViewItem *it = static_cast<KNHdrViewItem*>( currentItem() ); if (it && it->isActive()) { // take current article, if not selected if (it) it = static_cast<KNHdrViewItem*>(it->itemAbove()); else it = static_cast<KNHdrViewItem*>( firstChild() ); } if (it) { clearSelection(); setActive( it ); setSelectionAnchor( currentItem() ); } } void KNHeaderView::incCurrentArticle() { TQListViewItem *lvi = currentItem(); if ( lvi && lvi->isExpandable() ) lvi->setOpen( true ); if ( lvi && lvi->itemBelow() ) { setCurrentItem( lvi->itemBelow() ); ensureItemVisible( currentItem() ); setFocus(); } } void KNHeaderView::decCurrentArticle() { TQListViewItem *lvi = currentItem(); if ( lvi && lvi->itemAbove() ) { if ( lvi->itemAbove()->isExpandable() ) lvi->itemAbove()->setOpen( true ); setCurrentItem( lvi->itemAbove() ); ensureItemVisible( currentItem() ); setFocus(); } } void KNHeaderView::selectCurrentArticle() { clearSelection(); setActive( currentItem() ); } bool KNHeaderView::nextUnreadArticle() { if ( !knGlobals.groupManager()->currentGroup() ) return false; KNHdrViewItem *next, *current; KNRemoteArticle *art; current = static_cast<KNHdrViewItem*>( currentItem() ); if ( !current ) current = static_cast<KNHdrViewItem*>( firstChild() ); if(!current) return false; art = static_cast<KNRemoteArticle*>( current->art ); if ( !current->isActive() && !art->isRead() ) // take current article, if unread & not selected next = current; else { if ( current->isExpandable() && art->hasUnreadFollowUps() && !current->isOpen() ) setOpen( current, true ); next = static_cast<KNHdrViewItem*>( current->itemBelow() ); } while ( next ) { art = static_cast<KNRemoteArticle*>( next->art ); if ( !art->isRead() ) break; else { if ( next->isExpandable() && art->hasUnreadFollowUps() && !next->isOpen() ) setOpen( next, true ); next = static_cast<KNHdrViewItem*>( next->itemBelow() ); } } if ( next ) { clearSelection(); setActive( next ); setSelectionAnchor( currentItem() ); return true; } return false; } bool KNHeaderView::nextUnreadThread() { KNHdrViewItem *next, *current; KNRemoteArticle *art; if ( !knGlobals.groupManager()->currentGroup() ) return false; current = static_cast<KNHdrViewItem*>( currentItem() ); if ( !current ) current = static_cast<KNHdrViewItem*>( firstChild() ); if ( !current ) return false; art = static_cast<KNRemoteArticle*>( current->art ); if ( current->depth() == 0 && !current->isActive() && (!art->isRead() || art->hasUnreadFollowUps()) ) next = current; // take current article, if unread & not selected else next = static_cast<KNHdrViewItem*>( current->itemBelow() ); while ( next ) { art = static_cast<KNRemoteArticle*>( next->art ); if ( next->depth() == 0 ) { if ( !art->isRead() || art->hasUnreadFollowUps() ) break; } next = static_cast<KNHdrViewItem*>( next->itemBelow() ); } if ( next ) { setCurrentItem( next ); if ( art->isRead() ) nextUnreadArticle(); else { clearSelection(); setActive( next ); setSelectionAnchor( currentItem() ); } return true; } return false; } void KNHeaderView::toggleColumn( int column, int mode ) { bool *show = 0; int *col = 0; int width = 0; switch ( static_cast<KPaintInfo::ColumnIds>( column ) ) { case KPaintInfo::COL_SIZE: show = &mPaintInfo.showSize; col = &mPaintInfo.sizeCol; width = 42; break; case KPaintInfo::COL_SCORE: show = &mPaintInfo.showScore; col = &mPaintInfo.scoreCol; width = 42; break; default: return; } if ( mode == -1 ) *show = !*show; else *show = mode; mPopup->setItemChecked( column, *show ); if (*show) { header()->setResizeEnabled( true, *col ); setColumnWidth( *col, width ); } else { header()->setResizeEnabled( false, *col ); header()->setStretchEnabled( false, *col ); hideColumn( *col ); } if ( mode == -1 ) // save config when toggled writeConfig(); } void KNHeaderView::prepareForGroup() { mShowingFolder = false; header()->setLabel( mPaintInfo.senderCol, i18n("From") ); KNConfig::ReadNewsGeneral *rngConf = knGlobals.configManager()->readNewsGeneral(); toggleColumn( KPaintInfo::COL_SCORE, rngConf->showScore() ); } void KNHeaderView::prepareForFolder() { mShowingFolder = true; header()->setLabel( mPaintInfo.senderCol, i18n("Newsgroups / To") ); toggleColumn( KPaintInfo::COL_SCORE, false ); // local folders have no score } bool KNHeaderView::event( TQEvent *e ) { // we don't want to have the alternate list background restored // to the system defaults! if (e->type() == TQEvent::ApplicationPaletteChange) return TQListView::event(e); else return KListView::event(e); } void KNHeaderView::contentsMousePressEvent( TQMouseEvent *e ) { if (!e) return; bool selectMode=(( e->state() & ShiftButton ) || ( e->state() & ControlButton )); TQPoint vp = contentsToViewport(e->pos()); TQListViewItem *i = itemAt(vp); KListView::contentsMousePressEvent( e ); if ( i ) { int decoLeft = header()->sectionPos( 0 ) + treeStepSize() * ( (i->depth() - 1) + ( rootIsDecorated() ? 1 : 0) ); int decoRight = kMin( decoLeft + treeStepSize() + itemMargin(), header()->sectionPos( 0 ) + header()->sectionSize( 0 ) ); bool rootDecoClicked = vp.x() > decoLeft && vp.x() < decoRight; if( !selectMode && i->isSelected() && !rootDecoClicked ) setActive( i ); } } void KNHeaderView::contentsMouseDoubleClickEvent( TQMouseEvent *e ) { if (!e) return; TQListViewItem *i = itemAt( contentsToViewport(e->pos()) ); if (i) { emit doubleClick( i ); return; } KListView::contentsMouseDoubleClickEvent( e ); } void KNHeaderView::keyPressEvent(TQKeyEvent *e) { if (!e) return; TQListViewItem *i = currentItem(); switch(e->key()) { case Key_Space: case Key_Backspace: case Key_Delete: e->ignore(); // don't eat them break; case Key_Enter: case Key_Return: setActive( i ); break; default: KListView::keyPressEvent (e); } } TQDragObject* KNHeaderView::dragObject() { KNHdrViewItem *item = static_cast<KNHdrViewItem*>( itemAt(viewport()->mapFromGlobal(TQCursor::pos())) ); if (item) return item->dragObject(); else return 0; } void KNHeaderView::slotSizeChanged( int section, int, int newSize ) { viewport()->repaint( header()->sectionPos(section), 0, newSize, visibleHeight(), false); } bool KNHeaderView::eventFilter(TQObject *o, TQEvent *e) { if ((e->type() == TQEvent::KeyPress) && (TQT_TQKEYEVENT(e)->key() == Key_Tab)) { emit(focusChangeRequest(this)); if (!hasFocus()) // focusChangeRequest was successful return true; } // right click on header if ( e->type() == TQEvent::MouseButtonPress && TQT_TQMOUSEEVENT(e)->button() == Qt::RightButton && o->isA(TQHEADER_OBJECT_NAME_STRING) ) { mPopup->popup( TQT_TQMOUSEEVENT(e)->globalPos() ); return true; } return KListView::eventFilter(o, e); } void KNHeaderView::focusInEvent(TQFocusEvent *e) { TQListView::focusInEvent(e); emit focusChanged(e); } void KNHeaderView::focusOutEvent(TQFocusEvent *e) { TQListView::focusOutEvent(e); emit focusChanged(e); } void KNHeaderView::resetCurrentTime() { mDateFormatter.reset(); TQTimer::singleShot( 1000, this, TQT_SLOT(resetCurrentTime()) ); } //BEGIN: KNHeaderViewToolTip ================================================== KNHeaderViewToolTip::KNHeaderViewToolTip( KNHeaderView *parent ) : TQToolTip( parent->viewport() ), listView( parent ) { } void KNHeaderViewToolTip::maybeTip( const TQPoint &p ) { const KNHdrViewItem *item = static_cast<KNHdrViewItem*>( listView->itemAt( p ) ); if ( !item ) return; const int column = listView->header()->sectionAt( p.x() ); if ( column == -1 ) return; if ( !item->showToolTip( column ) ) return; const TQRect itemRect = listView->itemRect( item ); if ( !itemRect.isValid() ) return; const TQRect headerRect = listView->header()->sectionRect( column ); if ( !headerRect.isValid() ) return; tip( TQRect( headerRect.left(), itemRect.top(), headerRect.width(), itemRect.height() ), TQStyleSheet::escape( item->text( column ) ) ); } //END: KNHeaderViewToolTip ==================================================== #include "headerview.moc"