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.
618 lines
16 KiB
618 lines
16 KiB
/*
|
|
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 <kdeversion.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, Qt::AlignRight );
|
|
setColumnAlignment( mPaintInfo.scoreCol, Qt::AlignRight );
|
|
|
|
// due to our own column text squeezing we need to tqrepaint 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 ) {
|
|
KConfig *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()
|
|
{
|
|
KConfig *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 );
|
|
tqrepaintItem( 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()->tqrepaint( header()->sectionPos(section), 0, newSize, visibleHeight(), false);
|
|
}
|
|
|
|
|
|
bool KNHeaderView::eventFilter(TQObject *o, TQEvent *e)
|
|
{
|
|
if ((e->type() == TQEvent::KeyPress) && (static_cast<TQKeyEvent*>(e)->key() == Key_Tab)) {
|
|
emit(focusChangeRequest(this));
|
|
if (!hasFocus()) // focusChangeRequest was successful
|
|
return true;
|
|
}
|
|
|
|
// right click on header
|
|
if ( e->type() == TQEvent::MouseButtonPress &&
|
|
static_cast<TQMouseEvent*>(e)->button() == RightButton &&
|
|
o->isA(TQHEADER_OBJECT_NAME_STRING) )
|
|
{
|
|
mPopup->popup( static_cast<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 tqitemRect = listView->tqitemRect( item );
|
|
if ( !tqitemRect.isValid() )
|
|
return;
|
|
const TQRect headerRect = listView->header()->sectionRect( column );
|
|
if ( !headerRect.isValid() )
|
|
return;
|
|
|
|
tip( TQRect( headerRect.left(), tqitemRect.top(), headerRect.width(), tqitemRect.height() ),
|
|
TQStyleSheet::escape( item->text( column ) ) );
|
|
}
|
|
|
|
//END: KNHeaderViewToolTip ====================================================
|
|
|
|
#include "headerview.moc"
|