/******************************************************************************
*
* Copyright (C) 2002 Hugo PEREIRA <mailto: hugo.pereira@free.fr>
*
* This 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 software 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, see <http://www.gnu.org/licenses/>.
*
*******************************************************************************/

#include "MainWindow.h"

#include "Application.h"
#include "BaseStatusBar.h"
#include "ContextMenu.h"
#include "CustomToolBar.h"
#include "IconNames.h"
#include "IconEngine.h"
#include "InformationDialog.h"
#include "JobManager.h"
#include "JobManagerFrame.h"
#include "Menu.h"
#include "NewFrameDialog.h"
#include "QuestionDialog.h"
#include "QtUtil.h"
#include "JobInformationDialog.h"
#include "SummaryFrame.h"
#include "SignalJobsDialog.h"
#include "SignalMenu.h"
#include "Singleton.h"
#include "TreeView.h"
#include "UserSelectionFrame.h"
#include "UserSet.h"
#include "Util.h"
#include "XmlOptions.h"

#include <QTextStream>
#include <sys/types.h>
#include <signal.h>

//___________________________________________
class TabWidget: public QTabWidget
{

    public:

    //* constructor
    explicit TabWidget( QWidget* parent ):
        QTabWidget( parent )
    {}

    protected:

    //* tab insertion
    virtual void tabInserted( int index )
    {
        QTabWidget::tabInserted( index );
        int count( QTabWidget::count() );
        if( tabBar() ) tabBar()->setVisible( count > 1 );
    }

    //* tab insertion
    virtual void tabRemoved( int index )
    {
        QTabWidget::tabRemoved( index );
        int count( QTabWidget::count() );
        if( tabBar() ) tabBar()->setVisible( count > 1 );
    }

};

//___________________________________________
MainWindow::MainWindow( QWidget* parent ):
    BaseMainWindow( parent ),
    activeFrame_( 0 ),
    isPaused_( false ),
    refreshRate_( 0 )
{
    Debug::Throw( "MainWindow::MainWindow.\n" );

    setOptionName( "MAIN_WINDOW" );

    // install actions
    _installActions();
    Application& application( *static_cast<Application*>(Base::Singleton::get().application()) );
    addAction( &application.closeAction() );

    // statusBar
    setStatusBar( statusBar_ = new BaseStatusBar( this ) );
    statusBar_->addLabel(1);
    statusBar_->addClock();

    // toolbar
    userToolBar_ =  new CustomToolBar( tr( "User" ), this, "USER_TOOLBAR" );
    userToolBar_->addWidget( userFrame_ = new UserSelectionFrame( userToolBar_ ) );
    userFrame_->comboBox().setEditable( false );

    // add default users to user_selection frame
    UserSet users;
    users << UserSet::allUsers() << Util::user();
    userFrame_->updateUsers( users.get() );

    toolbar_ =  new CustomToolBar( tr( "Main Toolbar" ), this, "TOOLBAR" );
    toolbar_->addAction( &addFrameAction() );
    toolbar_->addSeparator();

    toolbar_->addAction( pauseAction_ );
    toolbar_->addAction( resumeAction_ );
    toolbar_->addAction( killAction_ );
    toolbar_->addAction( recordAction_ );
    toolbar_->addSeparator();

    toolbar_->addAction( updateAction_ );
    toolbar_->addAction( lockAction_ );

    // main widget and layout
    QWidget* main( new QWidget( this ) );
    main->setLayout( new QVBoxLayout );
    main->layout()->setMargin(0);
    main->layout()->setSpacing(0);
    setCentralWidget( main );

    // tab widget
    main->layout()->addWidget( tabWidget_ = new TabWidget( main ) );
    tabWidget_->setMovable( true );
    connect( tabWidget_, SIGNAL(currentChanged(int)), SLOT(_activeFrameChanged(int)) );

    // create removeTab button
    #if QT_VERSION < 0x040500
    {
        QToolButton* button = new QToolButton;
        button->setDefaultAction( removeFrameAction_ );
        button->setAutoRaise( true );
        tabWidget_->setCornerWidget( button, Qt::TopRightCorner );
    }
    #else
    tabWidget_->setTabsClosable( true );
    connect( tabWidget_, SIGNAL(tabCloseRequested(int)), SLOT(_removeFrame(int)));
    #endif

    // menu bar
    setMenuBar( new Menu( this ) );
    connect( userFrame_, SIGNAL(userChanged(QString)), SLOT(_updateUser(QString)) );

    // configuration update
    connect( &application, SIGNAL(configurationChanged()), SLOT(_updateConfiguration()) );
    connect( &application, SIGNAL(saveConfiguration()), SLOT(_saveConfiguration()) );
    connect( qApp, SIGNAL(aboutToQuit()), SLOT(_saveConfiguration()) );

    _updateConfiguration();
}

//_______________________________________
void MainWindow::updateActions()
{

    Debug::Throw( "MainWindow::updateActions.\n" );
    bool hasSelection( !( hasActiveFrame() && activeFrame().jobManager().selectedJobs().empty() ) );
    recordAction_->setEnabled( hasSelection );
    resumeAction_->setEnabled( hasSelection );
    pauseAction_->setEnabled( hasSelection );
    killAction_->setEnabled( hasSelection );
    signalMenu_->setEnabled( hasSelection );
    return;
}

//_______________________________________
void MainWindow::updateUsers( UserSet users )
{

    Debug::Throw( "MainWindow::updateUsers.\n" );
    userFrame_->updateUsers(users.get());
    if( hasActiveFrame() )
    { userFrame_->setUser( activeFrame().jobManager().user() ); }

}

//_______________________________________
void MainWindow::addFrame( const QString& user )
{
    Debug::Throw() << "MainWindow::addFrame - user: " << user << endl;

    // check whether it is already displayed or not
    for( const auto& frame:Base::KeySet<JobManagerFrame>( this ) )
    {
        if( user == frame->jobManager().user() )
        {
            tabWidget_->setCurrentWidget( frame );
            return;
        }
    }

    // create new Frame
    JobManagerFrame& frame( _newFrame() );
    frame.setUser( user );
    frame.summaryFrame().setVisible( toggleSummaryAction_->isChecked() );

    // add to tab widget and set as current
    tabWidget_->addTab( &frame, user );
    tabWidget_->setCurrentWidget( &frame );

}

//____________________________________________________________
void MainWindow::requestJobList()
{
    Debug::Throw( "MainWindow::requestJobList.\n" );
    emit commandRequested( JobCommand( JobCommand::Type::JobList ) );
}

//____________________________________________________________
void MainWindow::processJobList( Job::Set jobs )
{
    Debug::Throw( "MainWindow::processJobList.\n" );

    // update frames
    _updateFrames( jobs );

    // store local copy
    jobs_  = jobs;

    // update statusbar if active frame summary is hidden
    if( activeFrame().summaryFrame().isHidden() )
    { statusBar_->label().setTextAndUpdate( activeFrame().summaryFrame().summaryString() ); }

    // emit newjobs signal for records update
    _updateRecords( jobs );
    emit newJobs( jobs );

    // update actions
    updateActions();

    // restart timer
    if( lockAction_->isChecked() ) timer_.start( refreshRate_, this );

    return;
}

//____________________________________________________________
void MainWindow::closeJobInformationDialogs()
{
    Debug::Throw( "MainWindow::closeJobInformationDialog.\n" );

    // loop over record dialogs
    for( const auto& dialog:Base::KeySet<JobInformationDialog>( this ) )
    {

        if( !toggleRecordAction_->isChecked() )
        {

            // remove record
            records_.erase(
                std::remove_if(
                records_.begin(),
                records_.end(),
                JobRecord::SameIdFTor( dialog->record().id() ) ),
                records_.end() );

        }

        // close dialog
        dialog->close();
    }

}

//_______________________________________
void MainWindow::installFrames()
{

    Debug::Throw( "MainWindow::installFrames.\n" );

    // get users from options
    Options::List users( XmlOptions::get().specialOptions( "USER_NAME" ) );
    if( users.empty() ) users.append( Option( Util::user() ) );

    JobManagerFrame* firstFrame(0);
    for( const auto& option:users )
    {

        Debug::Throw() << "MainWindow::installFrames - adding user: " << option.raw() << endl;

        // create new Frame
        JobManagerFrame& frame( _newFrame() );
        if( !firstFrame) firstFrame= &frame;

        frame.setUser( option.raw() );
        frame.summaryFrame().setVisible( toggleSummaryAction_->isChecked() );
        tabWidget_->addTab( &frame, option.raw() );

    }

    _setActiveFrame( *firstFrame );
    firstFrame->jobManager().list().setFocus();
    tabWidget_->setCurrentIndex(0);

}

//_______________________________________
void MainWindow::_updateConfiguration()
{
    Debug::Throw( "MainWindow::_updateConfiguration.\n" );

    // needed to get proper size (from options) at startup
    resize( sizeHint() );

    // toggle summary
    toggleSummaryAction_->setChecked( XmlOptions::get().get<bool>( "SHOW_SUMMARY" ) );
    toggleRecordAction_->setChecked( XmlOptions::get().get<bool>( "RECORD_ALL_JOBS" ) );

    // tree view
    treeViewAction_->setChecked( XmlOptions::get().get<bool>("TREE_VIEW") );
    for( const auto& frame:Base::KeySet<JobManagerFrame>( this ) )
    { frame->jobManager().toggleTreeView( treeViewAction_->isChecked() ); }

    // refresh rate
    refreshRate_ = 1000 * XmlOptions::get().get<int>( "REFRESH_RATE" );

    Debug::Throw( "MainWindow::_updateConfiguration - done.\n" );


}

//________________________________________
void MainWindow::_saveConfiguration()
{
    Debug::Throw( "MainWindow::_saveConfiguration.\n" );

    XmlOptions::get().clearSpecialOptions( "USER_NAME" );

    for( int index = 0; index < tabWidget_->QTabWidget::count(); ++index )
    {
        if( JobManagerFrame* frame = qobject_cast<JobManagerFrame*>( tabWidget_->widget( index ) ) )
        { XmlOptions::get().add( "USER_NAME", frame->jobManager().user() ); }
    }

}

//___________________________________________________________
void MainWindow::_updateUser( QString user )
{

    Debug::Throw() << "MainWindow::_updateUser - " << user << endl;
    if( !hasActiveFrame() ) return;

    // check whether it is already displayed or not
    for( const auto& frame:Base::KeySet<JobManagerFrame>( this ) )
    {
        if( user == frame->jobManager().user() )
        {
            tabWidget_->setCurrentWidget( frame );
            return;
        }
    }

    tabWidget_->setTabText( tabWidget_->currentIndex(), user );
    activeFrame().setUser( user );

}

//_______________________________________________
void MainWindow::_resume()
{ _sendSignal( SIGCONT ); }

//_______________________________________________
void MainWindow::_pause()
{ _sendSignal( SIGSTOP ); }

//_______________________________________________
void MainWindow::_kill()
{ _sendSignal( SIGKILL ); }

//___________________________________________________________________________________
void MainWindow::_sendSignal( int signal )
{
    Debug::Throw( "MainWindow::_sendSignal.\n" );
    if( !hasActiveFrame() ) return;

    Job::List jobs;
    for( const auto& job:activeFrame().jobManager().selectedJobs() )
    { if( job.id() > 0 ) jobs.append( job ); }

    if( jobs.empty() ) return;
    if( !SignalJobsDialog( this, signal, jobs ).exec() ) return;

    // create command and append requested signal
    JobCommand command( JobCommand::Type::Signal );
    command << QString::number( signal );

    // add arguments to command
    for( const auto& job:jobs )
    {  if( job.id() > 0 ) command << QString::number( job.id() ); }

    if( !command.arguments().isEmpty() )
    {
        emit commandRequested( command );
        emit commandRequested( JobCommand( JobCommand::Type::JobList ) );
    }

}

//_______________________________________________
void MainWindow::_record()
{
    Debug::Throw( "MainWindow::_record.\n" );
    if( !hasActiveFrame() ) return;

    // get frame and selected jobs
    JobManager &frame( activeFrame().jobManager() );
    Job::List jobs( frame.selectedJobs() );
    if( jobs.empty() )
    {
        InformationDialog( this, tr( "No selected processes. <Record> canceled." ) ).exec();
        return;
    }

    // get list of associated dialogs
    Base::KeySet<JobInformationDialog> dialogs( this );

    // store jobs for which no record is found
    Job::Set invalid;

    // loop over jobs, find matching record
    for( const auto& job:jobs )
    {

        // try find matching job
        bool found( false );
        for( const auto& dialog:dialogs )
        {
            if( dialog->record().id() == job.id() )
            {
                dialog->uniconify();
                found = true;
                break;
            }
        }

        // move to next job
        if( found ) continue;

        // get matching job record
        auto recordIter( std::find_if( records_.begin(), records_.end(), JobRecord::SameIdFTor( job.id() ) ) );
        if( recordIter == records_.end() )
        {

            if( toggleRecordAction_->isChecked() )
            {
                invalid.insert( job );
                continue;
            } else recordIter = records_.insert( records_.end(), JobRecord( job ) );

        }

        // create a new JobInformationDialog
        JobInformationDialog *dialog = new JobInformationDialog( this );
        dialog->setJob( job, *recordIter );
        Base::Key::associate( this, dialog );

        // window title
        const QString buffer = QString( tr( "Process %1 (%2) - Top" ) ).arg( job.id() ).arg( job.longName().isEmpty() ? job.name():job.longName() );
        dialog->setWindowTitle( buffer );
        dialog->centerOnPointer();

        // connection
        if( !toggleRecordAction_->isChecked() )
        { connect( dialog, SIGNAL(closed(JobRecord)), SLOT(_recordDialogClosed(JobRecord)) ); }

        // connect for later updates
        connect( this, SIGNAL(newJobs(Job::Set)), dialog, SLOT(update(Job::Set)) );
        dialog->show();

    }

    // show dialog for invalid jobs
    if( !invalid.empty() )
    {
        QString buffer;
        if( invalid.size() == 1 ) buffer = QString( tr( "Process %1 has no record" ) ).arg( invalid.begin()->id() );
        else {
            buffer = QString( tr( "Following processes have no record:\n" ) );
            int index(0);
            for( const auto& job:invalid )
            {
                if( index && !(index%10) ) buffer += "\n";
                buffer += QString( " %1" ).arg( job.id() );
                ++index;
            }

        }

        InformationDialog( this, buffer ).exec();

    }

    return;

}

//_______________________________________________
void MainWindow::_toggleTreeView( bool value )
{

    Debug::Throw( "MainWindow::_toggleTreeView.\n" );
    if( hasActiveFrame() && activeFrame().jobManager().toggleTreeView( value ) )
    { XmlOptions::get().set<bool>( "TREE_VIEW", value ); }

}

//_______________________________________________
void MainWindow::_toggleRecordAllJobs( bool state )
{
    XmlOptions::get().set<bool>( "RECORD_ALL_JOBS", state );
    if( state ) _updateRecords( jobs_ );
    else records_.clear();
}

//_______________________________________________
void MainWindow::_toggleUpdate( bool state )
{
    Debug::Throw() << "MainWindow::_toggleUpdate - state: " << state << endl;
    if( state ) timer_.start( refreshRate_, this );
    else timer_.stop();

    return;
}

//_______________________________________
void MainWindow::_toggleSummary( bool value )
{

    Debug::Throw( "MainWindow::_toggleSummary.\n" );
    if( !hasActiveFrame() ) return;
    activeFrame().summaryFrame().setVisible( value );
    XmlOptions::get().set<bool>( "SHOW_SUMMARY", value );

    // clear label
    if( value ) statusBar_->label().clear();

}

//_______________________________________
void MainWindow::_activeFrameChanged( int index )
{

    Debug::Throw() << "MainWindow::_activeFrameChanged - index: " << index << endl;
    Q_ASSERT( index >= 0 );
    JobManagerFrame* frame = qobject_cast<JobManagerFrame*>( tabWidget_->widget( index ) );
    Q_CHECK_PTR( frame );

    // change frame activity and update
    _setActiveFrame( *frame );

    // update userSelectionFrame so that it displays the correct user name
    userFrame_->setUser( frame->jobManager().user() );

    // update actions
    treeViewAction_->setChecked( frame->jobManager().treeViewEnabled() );

    // update title
    QString buffer;
    if( frame->jobManager().user() != Util::user() ) buffer = QString( "%1 - Top" ).arg( frame->jobManager().user() );
    else buffer = "Top";
    setWindowTitle( buffer );

}

//_______________________________________
void MainWindow::_addFrame()
{
    Debug::Throw( "MainWindow::_addFrame.\n" );

    // create dialog
    NewFrameDialog dialog( this );

    // update user selection
    UserSet users;
    users << Util::user() << UserSet::allUsers();
    dialog.userSelectionFrame().updateUsers( users.get() );
    dialog.userSelectionFrame().updateUsers( userFrame_->users() );
    dialog.userSelectionFrame().setUser( userFrame_->user() );

    // adjust focus and map dialog
    dialog.userSelectionFrame().comboBox().setFocus();
    if( !dialog.centerOnWidget( window() ).exec() ) return;

    // get user and add
    addFrame( dialog.userSelectionFrame().user() );

}

//_______________________________________
void MainWindow::_removeFrame()
{ return _removeFrame( tabWidget_->currentIndex() ); }

//_______________________________________
void MainWindow::_removeFrame( int index )
{

    Debug::Throw( "MainWindow::_removeFrame.\n" );

    // check counts
    int counts( Base::KeySet<JobManagerFrame>( this ).size() );
    Q_ASSERT( counts > 1 && hasActiveFrame() );

    // check frame
    Q_ASSERT( index >= 0 );
    JobManagerFrame* frame = qobject_cast<JobManagerFrame*>( tabWidget_->widget( index ) );
    Q_CHECK_PTR( frame );

    const QString buffer = QString( tr( "Remove tab '%1' ?" ) ).arg( frame->jobManager().user() );
    if( !QuestionDialog( this, buffer ).centerOnWidget( window() ).exec() ) return;

    // change remove action enability
    if( counts == 2 )
    { removeFrameAction_->setEnabled( false ); }

    // change active frame
    const int currentIndex( tabWidget_->currentIndex() );
    if( index == currentIndex )
    { tabWidget_->setCurrentIndex( ( index > 0 ) ? index-1:index ); }

    // remove
    tabWidget_->removeTab( index );

    // delete old active frame
    frame->deleteLater();

}

//_______________________________________
void MainWindow::_recordDialogClosed( JobRecord record )
{
    Debug::Throw( "MainWindow::_recordDialogClosed.\n" );
    if( toggleRecordAction_->isChecked() ) return;

    records_.erase(
        std::remove_if(
        records_.begin(),
        records_.end(),
        JobRecord::SameIdFTor( record.id() ) ),
        records_.end() );

    return;

}


//____________________________________
void MainWindow::timerEvent( QTimerEvent *event )
{

    Debug::Throw( "MainWindow::timerEvent.\n" );
    if (event->timerId() == timer_.timerId() )
    {
        timer_.stop();
        emit commandRequested( JobCommand( JobCommand::Type::JobList ) );
    }

}

//____________________________________
void MainWindow::closeEvent( QCloseEvent *event )
{
    Debug::Throw( "MainWindow::closeEvent.\n" );
    _saveConfiguration();
    event->accept();
    qApp->quit();
}

//________________________________________
void MainWindow::_setActiveFrame( JobManagerFrame& frame )
{
    Debug::Throw( "MainWindow::_setActiveFrame.\n" );
    if( activeFrame_ == &frame ) return;
    activeFrame_ = &frame;

    // update actions
    updateActions();

}

//________________________________________
JobManagerFrame& MainWindow::_newFrame()
{

    Debug::Throw( "MainWindow::_newFrame.\n" );

    JobManagerFrame* frame( new JobManagerFrame );

    // add actions to list
    QMenu* menu = new ContextMenu( &frame->jobManager().list() );
    menu->addAction( &frame->jobManager().list().findAction() );
    menu->addAction( recordAction_ );
    menu->addMenu( signalMenu_ );

    connect( frame->jobManager().list().selectionModel(), SIGNAL(selectionChanged (QItemSelection,QItemSelection)), SLOT(updateActions()) );
    connect( &frame->jobManager().list(), SIGNAL(activated(QModelIndex)), SLOT(_record()) );
    connect( &frame->jobManager().list(), SIGNAL(expanded(QModelIndex)), SLOT(updateActions()) );
    connect( &frame->jobManager().list(), SIGNAL(collapsed(QModelIndex)), SLOT(updateActions()) );

    frame->jobManager().list().setFocus();
    Debug::Throw( "MainWindow::_newFrame - tree view toggle.\n" );
    frame->jobManager().toggleTreeView( treeViewAction_->isChecked() );

    Base::Key::associate( this, frame );

    // update remove action
    Debug::Throw( "MainWindow::_newFrame - changing removeFrame action.\n" );
    if( Base::KeySet<JobManagerFrame>( this ).size() > 1 )
    { removeFrameAction_->setEnabled( true ); }

    // ask for update
    Debug::Throw( "MainWindow::_newFrame - requesting job list.\n" );
    requestJobList();

    return *frame;
}

//________________________________________
void MainWindow::_updateFrames( const Job::Set& jobs )
{

    // split against users
    using JobSetMap = QMap<QString, Job::Set >;
    JobSetMap jobMap;
    for( const auto& job:jobs )
    { jobMap[job.user()].insert( job ); }

    // update all frames
    for( const auto& frame:Base::KeySet<JobManagerFrame>( this ) )
    {

        if( frame->jobManager().user().isEmpty() || UserSet::isAllUsers( frame->jobManager().user() ) )
        {

            frame->jobManager().processJobList( jobs );
            frame->summaryFrame().updateJobs( frame->jobManager().jobs() );

        } else {

            QString localUser( frame->jobManager().user().left(8) );
            frame->jobManager().processJobList( jobMap[localUser] );
            frame->summaryFrame().updateJobs( frame->jobManager().jobs() );

        }

    }
}

//_______________________________________________
void MainWindow::_updateRecords( Job::Set jobs )
{

    // update records with a matching job, erase the other ones.
    records_.erase(
        std::remove_if(
        records_.begin(),
        records_.end(),
        NoJobFTor( jobs ) ),
        records_.end() );

    // adds the jobs which have no existing records
    if( toggleRecordAction_->isChecked() )
    { for( const auto& job:jobs ) records_ << JobRecord( job ); }

};

//________________________________________
void MainWindow::_installActions()
{

    Debug::Throw( "MainWindow::_installActions.\n" );

    addAction( resumeAction_ = new QAction( IconEngine::get( IconNames::Resume ), tr( "Resume" ), this ) );
    resumeAction_->setToolTip( tr( "Resume selected processes" ) );
    connect( resumeAction_, SIGNAL(triggered()), SLOT(_resume()) );

    addAction( pauseAction_ = new QAction( IconEngine::get( IconNames::Pause ), tr( "Suspend" ), this ) );
    pauseAction_->setToolTip( tr( "Suspend selected processes" ) );
    connect( pauseAction_, SIGNAL(triggered()), SLOT(_pause()) );

    addAction( killAction_ = new QAction( IconEngine::get( IconNames::Kill ), tr( "Kill" ), this ) );
    killAction_->setToolTip( tr( "Kill selected processes" ) );
    connect( killAction_, SIGNAL(triggered()), SLOT(_kill()) );

    addAction( recordAction_ = new QAction( IconEngine::get( IconNames::Information ), tr( "Process Statistics" ), this ) );
    recordAction_->setToolTip( tr( "Monitor selected processes cpu and memory usage in separate dialog" ) );
    connect( recordAction_, SIGNAL(triggered()), SLOT(_record()) );

    addAction( toggleRecordAction_ = new QAction( IconEngine::get( IconNames::Information ), tr( "Save Statistics of all Processes" ), this ) );
    toggleRecordAction_->setToolTip( tr( "Keep track of all processes cpu and memory usage" ) );
    toggleRecordAction_->setCheckable( true );
    connect( toggleRecordAction_, SIGNAL(toggled(bool)), SLOT(_toggleRecordAllJobs(bool)) );

    addAction( updateAction_ = new QAction( IconEngine::get( IconNames::Reload ), tr( "Reload" ), this ) );
    updateAction_->setToolTip( tr( "Update process list" ) );
    connect( updateAction_, SIGNAL(triggered()), SLOT(requestJobList()) );

    addAction( treeViewAction_ = new QAction( IconEngine::get( IconNames::Tree ), tr( "Show Process Tree" ), this ) );
    treeViewAction_->setToolTip(tr(  "Show flat list or tree" ) );
    treeViewAction_->setCheckable( true );
    connect( treeViewAction_, SIGNAL(toggled(bool)), SLOT(_toggleTreeView(bool)) );

    addAction( lockAction_ = new QAction( IconEngine::get( IconNames::Lock ), tr( "Lock" ), this ) );
    lockAction_->setToolTip( tr( "Lock/unlock process list update" ) );
    lockAction_->setCheckable( true );
    connect( lockAction_, SIGNAL(toggled(bool)), SLOT(_toggleUpdate(bool)) );
    lockAction_->setChecked( true );

    addAction( toggleSummaryAction_ = new QAction( tr( "Show Summary Panel" ), this ) );
    toggleSummaryAction_->setToolTip( tr( "Show/hide summary pannel" ) );
    toggleSummaryAction_->setCheckable( true );
    toggleSummaryAction_->setChecked( true );
    connect( toggleSummaryAction_, SIGNAL(toggled(bool)), SLOT(_toggleSummary(bool)) );

    // add frame
    addAction( addFrameAction_ = new QAction( IconEngine::get( IconNames::AddTab ), tr( "Add Tab..." ), this ) );
    addFrameAction_->setToolTip( tr( "Add a new tab to display processes of a give user" ) );
    addFrameAction_->setShortcut( QKeySequence::AddTab );
    connect( addFrameAction_, SIGNAL(triggered()), SLOT(_addFrame()) );

    // remove frame
    addAction( removeFrameAction_ = new QAction( IconEngine::get( IconNames::RemoveTab ), tr( "Remove Tab" ), this ) );
    removeFrameAction_->setToolTip( tr( "Remove current tab" ) );
    removeFrameAction_->setShortcut( QKeySequence::Close );
    connect( removeFrameAction_, SIGNAL(triggered()), SLOT(_removeFrame()) );
    removeFrameAction_->setEnabled( false );

    // signals menu
    signalMenu_ = new SignalMenu( this );
    connect( signalMenu_, SIGNAL(signalRequested(int)), SLOT(_sendSignal(int)) );

}

//_______________________________________________
bool MainWindow::NoJobFTor::operator() ( JobRecord& record )
{

    auto iter( std::find_if( jobs_.begin(), jobs_.end(), Job::SameIdFTor( record.id() ) ) );
    if( iter == jobs_.end() ) return true;
    else {
        record.updateJob( *iter );
        jobs_.erase( iter );
        return false;
    }

}
