<commit_before>/*
    This file is part of Akregator.

    Copyright (C) 2007 <NAME> <frank.osterfeld at kdemail.net>

    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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    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, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include "selectioncontroller.h"

#include "actionmanager.h"
#include "article.h"
#include "articlemodel.h"
#include "feedlist.h"
#include "subscriptionlistmodel.h"
#include "treenode.h"

#include <KRandom>

#include <QAbstractItemView>
#include <QItemSelectionModel>
#include <QMenu>
#include <QTimer>

namespace {
    static Akregator::Article articleForIndex( const QModelIndex& index, Akregator::FeedList* feedList )
    {
        if ( !index.isValid() )
            return Akregator::Article();

        const QString guid = index.data( Akregator::ArticleModel::GuidRole ).toString();
        const QString feedId = index.data( Akregator::ArticleModel::FeedIdRole ).toString();
        return feedList->findArticle( feedId, guid );
    }

    static QList<Akregator::Article> articlesForIndexes( const QModelIndexList& indexes, Akregator::FeedList* feedList )
    {
        QList<Akregator::Article> articles;
        Q_FOREACH ( const QModelIndex i, indexes )
        {
            articles.append( articleForIndex( i, feedList ) );
        }

        return articles;
    }

    static Akregator::TreeNode* subscriptionForIndex( const QModelIndex& index, Akregator::FeedList* feedList )
    {
        if ( !index.isValid() )
            return 0L;

         return feedList->findByID( index.data( Akregator::SubscriptionListModel::SubscriptionIdRole ).toInt() );
    }
} // anon namespace 

Akregator::SelectionController::SelectionController( QObject* parent ) : AbstractSelectionController( parent ), m_feedList( 0 ), m_feedSelector( 0 ), m_articleLister( 0 ), m_singleDisplay( 0 ), m_subscriptionModel ( 0 ), m_folderExpansionHandler( 0 ), m_selectedSubscription( 0 )
{
    m_articleFetchTimer = new QTimer( this );
}


void Akregator::SelectionController::setFeedSelector( QAbstractItemView* feedSelector )
{
    if ( m_feedSelector )
    {
        disconnect( m_feedSelector, SIGNAL( customContextMenuRequested( QPoint ) ), 
                    this, SLOT( subscriptionContextMenuRequested( QPoint ) ) );

        if ( m_feedSelector->selectionModel() )
        {
            disconnect( m_feedSelector->selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ),
                        this, SLOT( selectedSubscriptionChanged( QModelIndex ) ) );
        }
    }
    m_feedSelector = feedSelector;
    setUp();
}

void Akregator::SelectionController::setArticleLister( Akregator::ArticleLister* lister )
{
    if ( m_articleLister )
    {
        disconnect( m_articleLister->articleSelectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ),
                 this, SLOT( currentArticleIndexChanged( QModelIndex ) ) );
    }

    m_articleLister = lister;
    setUp();
}

void Akregator::SelectionController::setSingleArticleDisplay( Akregator::SingleArticleDisplay* display )
{
    m_singleDisplay = display;
}

Akregator::Article Akregator::SelectionController::currentArticle() const
{
    return ::articleForIndex( m_articleLister->articleSelectionModel()->currentIndex(), m_feedList );
}

QList<Akregator::Article> Akregator::SelectionController::selectedArticles() const
{
    return ::articlesForIndexes( m_articleLister->articleSelectionModel()->selectedIndexes(), m_feedList );
}

Akregator::TreeNode* Akregator::SelectionController::selectedSubscription() const
{
    return ::subscriptionForIndex( m_feedSelector->selectionModel()->currentIndex(), m_feedList );
}

void Akregator::SelectionController::setFeedList( Akregator::FeedList* list )
{
    m_feedList = list;
    setUp();
}

void Akregator::SelectionController::setFolderExpansionHandler( Akregator::FolderExpansionHandler* handler )
{
    m_folderExpansionHandler = handler;
    if ( !handler )
        return;
    handler->setFeedList( m_feedList );
    handler->setModel( m_subscriptionModel );
}

void Akregator::SelectionController::setUp()
{
    if ( !m_feedList || !m_feedSelector || !m_articleLister )
        return;

    m_subscriptionModel = new SubscriptionListModel( m_feedList, this );
    if ( m_folderExpansionHandler )
    {
        m_folderExpansionHandler->setFeedList( m_feedList );
        m_folderExpansionHandler->setModel( m_subscriptionModel );
    }
    m_feedSelector->setModel( m_subscriptionModel );

    connect( m_feedSelector->selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ),
             this, SLOT( selectedSubscriptionChanged( QModelIndex ) ) );

    connect( m_feedSelector, SIGNAL( customContextMenuRequested( QPoint ) ), 
             this, SLOT( subscriptionContextMenuRequested( QPoint ) ) );

    if ( m_articleLister->itemView() )
    {
        connect( m_articleLister->itemView(), SIGNAL( doubleClicked( QModelIndex ) ), this, SLOT( articleIndexDoubleClicked( QModelIndex ) )  );
    }
}

void Akregator::SelectionController::articleHeadersAvailable()
{
    m_articleLister->setArticleModel( new Akregator::ArticleModel( m_selectedSubscription ) );

    m_articleLister->setIsAggregation( m_selectedSubscription->isAggregation() );

    connect( m_articleLister->articleSelectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex) ),
             this, SLOT( currentArticleIndexChanged( QModelIndex ) ) );
}

void Akregator::SelectionController::selectedSubscriptionChanged( const QModelIndex& index )
{
    if ( !index.isValid() )
        return;

    m_selectedSubscription = selectedSubscription();
    emit currentSubscriptionChanged( m_selectedSubscription );

    // using a timer here internally to simulate async data fetching (which is still synchronous), 
    // to ensure the UI copes with async behavior later on

    if ( m_articleFetchTimer->isActive() ) 
        m_articleFetchTimer->stop(); // to come: kill running list job

    m_articleFetchTimer->setInterval( KRandom::random() % 400 );
    m_articleFetchTimer->setSingleShot( true );<commit_msg>Fix Akregator opening two konqueror on double click, feed list context menu appearing twice BUGS:152703<commit_after>/*
    This file is part of Akregator.

    Copyright (C) 2007 <NAME> <frank.osterfeld at kdemail.net>

    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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    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, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include "selectioncontroller.h"

#include "actionmanager.h"
#include "article.h"
#include "articlemodel.h"
#include "feedlist.h"
#include "subscriptionlistmodel.h"
#include "treenode.h"

#include <KRandom>

#include <QAbstractItemView>
#include <QItemSelectionModel>
#include <QMenu>
#include <QTimer>

namespace {
    static Akregator::Article articleForIndex( const QModelIndex& index, Akregator::FeedList* feedList )
    {
        if ( !index.isValid() )
            return Akregator::Article();

        const QString guid = index.data( Akregator::ArticleModel::GuidRole ).toString();
        const QString feedId = index.data( Akregator::ArticleModel::FeedIdRole ).toString();
        return feedList->findArticle( feedId, guid );
    }

    static QList<Akregator::Article> articlesForIndexes( const QModelIndexList& indexes, Akregator::FeedList* feedList )
    {
        QList<Akregator::Article> articles;
        Q_FOREACH ( const QModelIndex i, indexes )
        {
            articles.append( articleForIndex( i, feedList ) );
        }

        return articles;
    }

    static Akregator::TreeNode* subscriptionForIndex( const QModelIndex& index, Akregator::FeedList* feedList )
    {
        if ( !index.isValid() )
            return 0L;

         return feedList->findByID( index.data( Akregator::SubscriptionListModel::SubscriptionIdRole ).toInt() );
    }
} // anon namespace 

Akregator::SelectionController::SelectionController( QObject* parent ) : AbstractSelectionController( parent ), m_feedList( 0 ), m_feedSelector( 0 ), m_articleLister( 0 ), m_singleDisplay( 0 ), m_subscriptionModel ( 0 ), m_folderExpansionHandler( 0 ), m_selectedSubscription( 0 )
{
    m_articleFetchTimer = new QTimer( this );
}


void Akregator::SelectionController::setFeedSelector( QAbstractItemView* feedSelector )
{
    if ( m_feedSelector )
    {
        disconnect( m_feedSelector, SIGNAL( customContextMenuRequested( QPoint ) ), 
                    this, SLOT( subscriptionContextMenuRequested( QPoint ) ) );

        if ( m_feedSelector->selectionModel() )
        {
            disconnect( m_feedSelector->selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ),
                        this, SLOT( selectedSubscriptionChanged( QModelIndex ) ) );
        }
    }
    m_feedSelector = feedSelector;

    connect( m_feedSelector, SIGNAL( customContextMenuRequested( QPoint ) ), 
             this, SLOT( subscriptionContextMenuRequested( QPoint ) ) );
    setUp();
}

void Akregator::SelectionController::setArticleLister( Akregator::ArticleLister* lister )
{
    if ( m_articleLister )
    {
        disconnect( m_articleLister->articleSelectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ),
                 this, SLOT( currentArticleIndexChanged( QModelIndex ) ) );
    }

    m_articleLister = lister;

    if ( m_articleLister->itemView() )
    {
        connect( m_articleLister->itemView(), SIGNAL( doubleClicked( QModelIndex ) ), this, SLOT( articleIndexDoubleClicked( QModelIndex ) )  );
    }
}

void Akregator::SelectionController::setSingleArticleDisplay( Akregator::SingleArticleDisplay* display )
{
    m_singleDisplay = display;
}

Akregator::Article Akregator::SelectionController::currentArticle() const
{
    return ::articleForIndex( m_articleLister->articleSelectionModel()->currentIndex(), m_feedList );
}

QList<Akregator::Article> Akregator::SelectionController::selectedArticles() const
{
    return ::articlesForIndexes( m_articleLister->articleSelectionModel()->selectedIndexes(), m_feedList );
}

Akregator::TreeNode* Akregator::SelectionController::selectedSubscription() const
{
    return ::subscriptionForIndex( m_feedSelector->selectionModel()->currentIndex(), m_feedList );
}

void Akregator::SelectionController::setFeedList( Akregator::FeedList* list )
{
    m_feedList = list;
    setUp();
}

void Akregator::SelectionController::setFolderExpansionHandler( Akregator::FolderExpansionHandler* handler )
{
    m_folderExpansionHandler = handler;
    if ( !handler )
        return;
    handler->setFeedList( m_feedList );
    handler->setModel( m_subscriptionModel );
}

void Akregator::SelectionController::setUp()
{
    if ( !m_feedList || !m_feedSelector )
        return;

    //FIXME: Make sure that setUp is only run once (or that we're not connecting more than once)

    m_subscriptionModel = new SubscriptionListModel( m_feedList, this );
    if ( m_folderExpansionHandler )
    {
        m_folderExpansionHandler->setFeedList( m_feedList );
        m_folderExpansionHandler->setModel( m_subscriptionModel );
    }
    m_feedSelector->setModel( m_subscriptionModel );

    connect( m_feedSelector->selectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex ) ),
             this, SLOT( selectedSubscriptionChanged( QModelIndex ) ) );
}

void Akregator::SelectionController::articleHeadersAvailable()
{
    m_articleLister->setArticleModel( new Akregator::ArticleModel( m_selectedSubscription ) );

    m_articleLister->setIsAggregation( m_selectedSubscription->isAggregation() );

    connect( m_articleLister->articleSelectionModel(), SIGNAL( currentChanged( QModelIndex, QModelIndex) ),
             this, SLOT( currentArticleIndexChanged( QModelIndex ) ) );
}

void Akregator::SelectionController::selectedSubscriptionChanged( const QModelIndex& index )
{
    if ( !index.isValid() )
        return;

    m_selectedSubscription = selectedSubscription();
    emit currentSubscriptionChanged( m_selectedSubscription );

    // using a timer here internally to simulate async data fetching (which is still synchronous), 
    // to ensure the UI copes with async behavior later on

    if ( m_articleFetchTimer->isActive() ) 
        m_articleFetchTimer->stop(); // to come: kill running list job

    m_articleFetchTimer->setInterval( KRandom::random() % 400 );
    m_articleFetchTimer->setSingleShot( true );
{'fraction_non_alphanumeric': 0.09694279372807331, 'fraction_numerical': 0.0042958401947447555, 'mean_word_length': 4.060869565217391, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '350272', 'n_tokens_mistral': 3991, 'n_tokens_neox': 3832, 'n_words': 1009}