<commit_before>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (c) 2010 Arjen Hiemstra <ahiemstra@heimr.nl>
 * Copyright (c) 2011 Laszlo Papp <djszapi@archlinux.us>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "projectdock.h"

#include "filemanager.h"
#include "historymanager.h"
#include "newobjectcommand.h"
#include "objectmanager.h"
#include "selectionmanager.h"
#include "models/projectmodel.h"
#include "models/models.h"
#include "models/modeltest.h"

#include "lib/selectionmanager.h"
#include "lib/selectionmanager.h"
#include "objectmanager.h"
#include "filemanager.h"
#include "historymanager.h"
#include "newobjectcommand.h"

#include <core/debughelper.h>
#include <core/gluon_global.h>
#include <engine/asset.h>
#include <engine/game.h>
#include <engine/gameobject.h>
#include <engine/gameproject.h>
#include <engine/scene.h>

#include <KDE/KIcon>
#include <KDE/KInputDialog>
#include <KDE/KLocalizedString>
#include <KDE/KMessageBox>
#include <KDE/KRun>
#include <KDE/KStandardDirs>
#include <KDE/KToolBar>
#include <KDE/KFileItemListProperties>
#include <KDE/KFileItemActions>
#include <KDE/KFileDialog>

#include <QtGui/QMenu>
#include <QtGui/QTreeView>
#include <QtGui/QVBoxLayout>
#include <QtGui/QToolButton>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>

using namespace GluonCreator;

class ProjectDock::ProjectDockPrivate
{
    public:
        ProjectDockPrivate( ProjectDock* parent )
        {
            DEBUG_BLOCK
            q = parent;
            view = 0;
            GluonEngine::Asset* theItem;
            const QHash<QString, const QMetaObject*> types = GluonCore::GluonObjectFactory::instance()->objectTypes();
            QHash<QString, const QMetaObject*>::const_iterator i;
            for( i = types.constBegin(); i != types.constEnd(); ++i )
            {
                theItem = qobject_cast<GluonEngine::Asset*>( i.value()->newInstance() );
                if( theItem )
                {
                    const QList<GluonEngine::AssetTemplate*> templates = theItem->templates();
                    for( int j = 0; j < templates.length(); ++j )
                    {
                        assetTemplates.append( templates[j] );
                    }
                }
                else
                {
                    QObject* obj = i.value()->newInstance();
                    if( obj )
                    {
                        if( obj->inherits( "GluonEngine::Asset" ) )
                        {
                            DEBUG_TEXT2( "The Asset class %1 is lacking the Q_INTERFACES(GluonEngine::Asset) macro", i.value()->className() );
                        }
                    }
                }
            }
        }
        ProjectDock* q;
        ProjectModel* model;
        QTreeView* view;
        KToolBar* toolBar;
        QMenu* newMenu;

        QModelIndex currentContextIndex;
        void menuForObject( QModelIndex index, QMenu* menu );
        QList<QAction*> currentContextMenu;
        QList<GluonEngine::AssetTemplate*> assetTemplates;
};

void ProjectDock::ProjectDockPrivate::menuForObject( QModelIndex index, QMenu* menu )
{
    GluonCore::GluonObject* object = static_cast<GluonCore::GluonObject*>( index.internalPointer() );
    if( object )
    {
        const QMetaObject* mobj = object->metaObject();
        if( mobj )
        {
            currentContextIndex = index;
            QAction* action;
            if( object->inherits( "GluonEngine::Asset" ) )
            {
                GluonEngine::Asset* asset = qobject_cast< GluonEngine::Asset* >( object );
                if( asset )
                {
                    if(!asset->inherits("GluonEngine::Scene"))
                    {
                        KFileItem item(KFileItem::Unknown, KFileItem::Unknown, KUrl(asset->absolutePath()));
                        KFileItemListProperties properties(KFileItemList() << item );
                        KFileItemActions* openWithActions = new KFileItemActions(menu);
                        openWithActions->setItemListProperties(properties);

                        openWithActions->addOpenWithActionsTo(menu, QString());
                    }

                    menu->addSeparator();

                    QList<QAction*> actions = asset->actions();
                    foreach( QAction * action, actions )
                    {
                        connect( action, SIGNAL( triggered( bool ) ), model, SIGNAL( layoutChanged() ) );
                        menu->addAction(action);
                    }
                }
            }
            else
            {
                menu->addActions(newMenu->actions());
            }

            if( !object->inherits( "GluonEngine::GameProject" ) )
            {
                menu->addSeparator();

                action = new QAction( KIcon( "edit-delete" ), i18n( "Delete \"%1\"...", object->name() ), this->q );
                connect( action, SIGNAL( triggered() ), q, SLOT( deleteActionTriggered() ) );
                menu->addAction( action );
            }
        }
    }
}

ProjectDock::ProjectDock( const QString& title, QWidget* parent, Qt::WindowFlags flags )
    : QDockWidget( title, parent, flags ), d( new ProjectDockPrivate( this ) )
{
    setObjectName( "ProjectDock" );

    d->model = Models::instance()->projectModel();
    new ModelTest( d->model, this );

    d->view = new QTreeView( this );
    d->view->setModel( d->model );
    d->view->setHeaderHidden( true );
    d->view->setDragEnabled( true );
    d->view->setAcceptDrops( true );
    d->view->setContextMenuPolicy( Qt::CustomContextMenu );
    d->view->setEditTriggers( QAbstractItemView::NoEditTriggers );
    connect( d->view, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( showContextMenuRequested( const QPoint& ) ) );
    connect( d->view->selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( selectionChanged( QItemSelection, QItemSelection ) ) );

    d->model->setProject( GluonEngine::Game::instance()->gameProject() );
    connect( GluonEngine::Game::instance(), SIGNAL( currentProjectChanged( GluonEngine::GameProject* ) ), SLOT( currentProjectChanged( GluonEngine::GameProject* ) ) );
    connect( d->view, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( activated( QModelIndex ) ) );

    QWidget* widget = new QWidget( this );
    QVBoxLayout* layout = new QVBoxLayout();
    layout->setContentsMargins( 0, 0, 0, 0 );
    layout->setSpacing( 0 );
    widget->setLayout( layout );

    d->toolBar = new KToolBar( widget );
    d->toolBar->setIconDimensions( 16 );

    d->newMenu = new QMenu(i18n("New"), d->toolBar);

    QToolButton* menuButton = new QToolButton(d->toolBar);
    menuButton->setIcon(KIcon("document-new"));
    menuButton->setText(i18n("Add..."));
    menuButton->setPopupMode(QToolButton::InstantPopup);
    menuButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    menuButton->setMenu(d->newMenu);

    d->toolBar->insertWidget(0, menuButton);

    d->newMenu->addAction( KIcon( "folder" ), i18n( "New Folder" ), this, SLOT( newSubMenuTriggered() ) );
    d->newMenu->addAction( KIcon( "document-new" ), i18n( "New Scene" ), ObjectManager::instance(), SLOT( createNewScene() ) );
    d->newMenu->addSeparator();

    // Run through all the templates and add an action for each...
    foreach( const GluonEngine::AssetTemplate * item, d->assetTemplates )
    {
        QAction* action = d->newMenu->addAction( i18n( "New %1" ).arg( item->name ), this, SLOT( newAssetTriggered() ));
        action->setProperty( "newAssetClassname", QString( item->parent()->metaObject()->className() ) );
        action->setProperty( "newAssetName", item->name );
        action->setProperty( "newAssetPluginname", item->pluginname );
        action->setProperty( "newAssetFilename", item->filename );
    }

    d->newMenu->addSeparator();
    d->newMenu->addAction( KIcon("document-import"), i18n("Import Assets..."), this, SLOT(importAssetsTriggered()) );

    d->toolBar->addAction( KIcon( "edit-delete" ), i18nc( "Delete selected object from project", "Delete" ), this, SLOT( deleteActionTriggered() ) );

    layout->addWidget( d->toolBar );
    layout->addWidget( d->view );

    setWidget( widget );
}

ProjectDock::~ProjectDock()
{
    delete d;
}

void ProjectDock::activated( QModelIndex index )
{
    DEBUG_FUNC_NAME
    if( !index.isValid() )
    {
        return;
    }

    QObject* obj = static_cast<QObject*>( index.internalPointer() );
    if( !obj )
    {
        return;
    }

    GluonEngine::Scene* scene = qobject_cast<GluonEngine::Scene*>( obj );
    GluonEngine::Asset* asset = qobject_cast<GluonEngine::Asset*>( obj );
    // Scene's a special asset, so we check for that first
    if( scene )
    {
        if( GluonEngine::Game::instance()->currentScene() != scene )
        {
            GluonEngine::Game::instance()->setCurrentScene( scene );
            GluonEngine::Game::instance()->initializeAll();
            GluonEngine::Game::instance()->drawAll();
        }
    }
    else if( asset )
    {
        FileManager::instance()->openAsset( asset );
    }
}

void ProjectDock::selectionChanged( const QItemSelection& selected, const QItemSelection& /* deselected */ )
{
    SelectionManager::SelectionList selection;
    foreach( const QItemSelectionRange & range, selected )
    {
        foreach( const QModelIndex & index, range.indexes() )
        {
            GluonCore::GluonObject* obj = static_cast<GluonCore::GluonObject*>( index.internalPointer() );
            selection.append( obj );
        }
    }
    SelectionManager::instance()->setSelection( selection );
}

void ProjectDock::currentProjectChanged( GluonEngine::GameProject* project )
{
    d->model->setProject( project );

    if( !d->toolBar->isEnabled() )
        d->toolBar->setEnabled( true );

    d->view->expandAll();
}

void ProjectDock::showContextMenuRequested( const QPoint& pos )
{
    QModelIndex index = d->view->indexAt( pos );
    if( !index.isValid() )
        index = d->model->index( 0, 0 );

    QMenu menu( static_cast<GluonCore::GluonObject*>( index.internalPointer() )->name(), this );
    d->menuForObject(index, &menu);
    menu.exec( d->view->mapToGlobal( pos ) );
    connect( &menu, SIGNAL( aboutToHide() ), SLOT( contextMenuHiding() ) );
}

void ProjectDock::contextMenuHiding()
{
    d->currentContextIndex = QModelIndex();
}

void ProjectDock::deleteActionTriggered()
{
    DEBUG_BLOCK
    if( !d->currentContextIndex.isValid() )
        d->currentContextIndex = d->view->selectionModel()->currentIndex();

    if( !d->currentContextIndex.isValid() )
        return;

    GluonCore::GluonObject* object = static_cast<GluonCore::GluonObject*>( d->currentContextIndex.internalPointer() );
    DEBUG_TEXT( QString( "Requested deletion of %1" ).arg( object->fullyQualifiedName() ) );
    if( KMessageBox::questionYesNo( this, i18n( "Are you sure you wish to delete %1?\nThis will delete the item and all its children!" ).arg( object->name() ), i18n( "Really Delete?" ) ) == KMessageBox::Yes )
    {
		GluonEngine::Asset* asset = qobject_cast<GluonEngine::Asset*>( object );
		if( asset )
        	ObjectManager::instance()->assetDeleted( asset );

        d->view->selectionModel()->select( d->currentContextIndex.parent(), QItemSelectionModel::ClearAndSelect );
        d->model->removeRows( d->currentContextIndex.row(), 1, d->currentContextIndex.parent() );

        d->currentContextIndex = QModelIndex();
    }
}

void ProjectDock::newSubMenuTriggered()
{
    if( !d->currentContextIndex.isValid() )
        d->currentContextIndex = d->model->index(0, 0);

    QModelIndex newFolder = d->model->addChild( new GluonCore::GluonObject( i18n("New Folder") ), d->currentContextIndex );
    d->view->edit(newFolder);
}

void GluonCreator::ProjectDock::newAssetTriggered()
{
    if( !d->currentContextIndex.isValid() )
        d->currentContextIndex = d->model->index(0, 0);

    QAction* menuItem = qobject_cast< QAction* >( QObject::sender() );
    if( menuItem )
    {
        QString templateFilename = QString( "gluon/templates/%1/%2" ).arg( menuItem->property( "newAssetPluginname" ).toString() ).arg( menuItem->property( "newAssetFilename" ).toString() );
        QString fileName = GluonCore::Global::dataDirectory() + '/' + templateFilename;
        if( fileName.isEmpty() )
        {
            DEBUG_BLOCK
            DEBUG_TEXT( "Failed at finding the template file!" );
            return;
        }

        GluonEngine::Asset* newAsset = ObjectManager::instance()->createNewAsset(fileName, static_cast<GluonCore::GluonObject*>( d->currentContextIndex.internalPointer() ),
                                                                                menuItem->property( "newAssetClassname" ).toString(),
                                                                                menuItem->property( "newAssetName" ).toString() );

        if( newAsset )
        {
            d->model->addChild( newAsset, d->currentContextIndex );
        }
    }
}

void ProjectDock::importAssetsTriggered()
{
    ObjectManager::instance()->createAssets(KFileDialog::getOpenFileNames(), static_cast<GluonCore::GluonObject*>( d->currentContextIndex.internalPointer() ));
}
<commit_msg>Creator: Check whether a class uses refcounting, and if so, check whether refs == 0 before deleting.<commit_after>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (c) 2010 Arjen Hiemstra <ahiemstra@heimr.nl>
 * Copyright (c) 2011 Laszlo Papp <djszapi@archlinux.us>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "projectdock.h"

#include "filemanager.h"
#include "historymanager.h"
#include "newobjectcommand.h"
#include "objectmanager.h"
#include "selectionmanager.h"
#include "models/projectmodel.h"
#include "models/models.h"
#include "models/modeltest.h"

#include "lib/selectionmanager.h"
#include "lib/selectionmanager.h"
#include "objectmanager.h"
#include "filemanager.h"
#include "historymanager.h"
#include "newobjectcommand.h"

#include <core/debughelper.h>
#include <core/gluon_global.h>
#include <engine/asset.h>
#include <engine/game.h>
#include <engine/gameobject.h>
#include <engine/gameproject.h>
#include <engine/scene.h>

#include <KDE/KIcon>
#include <KDE/KInputDialog>
#include <KDE/KLocalizedString>
#include <KDE/KMessageBox>
#include <KDE/KRun>
#include <KDE/KStandardDirs>
#include <KDE/KToolBar>
#include <KDE/KFileItemListProperties>
#include <KDE/KFileItemActions>
#include <KDE/KFileDialog>

#include <QtGui/QMenu>
#include <QtGui/QTreeView>
#include <QtGui/QVBoxLayout>
#include <QtGui/QToolButton>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>

using namespace GluonCreator;

class ProjectDock::ProjectDockPrivate
{
    public:
        ProjectDockPrivate( ProjectDock* parent )
        {
            DEBUG_BLOCK
            q = parent;
            view = 0;
            GluonEngine::Asset* theItem;
            const QHash<QString, const QMetaObject*> types = GluonCore::GluonObjectFactory::instance()->objectTypes();
            QHash<QString, const QMetaObject*>::const_iterator i;
            for( i = types.constBegin(); i != types.constEnd(); ++i )
            {
                theItem = qobject_cast<GluonEngine::Asset*>( i.value()->newInstance() );
                if( theItem )
                {
                    const QList<GluonEngine::AssetTemplate*> templates = theItem->templates();
                    for( int j = 0; j < templates.length(); ++j )
                    {
                        assetTemplates.append( templates[j] );
                    }
                }
                else
                {
                    QObject* obj = i.value()->newInstance();
                    if( obj )
                    {
                        if( obj->inherits( "GluonEngine::Asset" ) )
                        {
                            DEBUG_TEXT2( "The Asset class %1 is lacking the Q_INTERFACES(GluonEngine::Asset) macro", i.value()->className() );
                        }
                    }
                }
            }
        }
        ProjectDock* q;
        ProjectModel* model;
        QTreeView* view;
        KToolBar* toolBar;
        QMenu* newMenu;

        QModelIndex currentContextIndex;
        void menuForObject( QModelIndex index, QMenu* menu );
        QList<QAction*> currentContextMenu;
        QList<GluonEngine::AssetTemplate*> assetTemplates;
};

void ProjectDock::ProjectDockPrivate::menuForObject( QModelIndex index, QMenu* menu )
{
    GluonCore::GluonObject* object = static_cast<GluonCore::GluonObject*>( index.internalPointer() );
    if( object )
    {
        const QMetaObject* mobj = object->metaObject();
        if( mobj )
        {
            currentContextIndex = index;
            QAction* action;
            if( object->inherits( "GluonEngine::Asset" ) )
            {
                GluonEngine::Asset* asset = qobject_cast< GluonEngine::Asset* >( object );
                if( asset )
                {
                    if(!asset->inherits("GluonEngine::Scene"))
                    {
                        KFileItem item(KFileItem::Unknown, KFileItem::Unknown, KUrl(asset->absolutePath()));
                        KFileItemListProperties properties(KFileItemList() << item );
                        KFileItemActions* openWithActions = new KFileItemActions(menu);
                        openWithActions->setItemListProperties(properties);

                        openWithActions->addOpenWithActionsTo(menu, QString());
                    }

                    menu->addSeparator();

                    QList<QAction*> actions = asset->actions();
                    foreach( QAction * action, actions )
                    {
                        connect( action, SIGNAL( triggered( bool ) ), model, SIGNAL( layoutChanged() ) );
                        menu->addAction(action);
                    }
                }
            }
            else
            {
                menu->addActions(newMenu->actions());
            }

            if( !object->inherits( "GluonEngine::GameProject" ) )
            {
                menu->addSeparator();

                action = new QAction( KIcon( "edit-delete" ), i18n( "Delete \"%1\"...", object->name() ), this->q );
                connect( action, SIGNAL( triggered() ), q, SLOT( deleteActionTriggered() ) );
                menu->addAction( action );
            }
        }
    }
}

ProjectDock::ProjectDock( const QString& title, QWidget* parent, Qt::WindowFlags flags )
    : QDockWidget( title, parent, flags ), d( new ProjectDockPrivate( this ) )
{
    setObjectName( "ProjectDock" );

    d->model = Models::instance()->projectModel();
    new ModelTest( d->model, this );

    d->view = new QTreeView( this );
    d->view->setModel( d->model );
    d->view->setHeaderHidden( true );
    d->view->setDragEnabled( true );
    d->view->setAcceptDrops( true );
    d->view->setContextMenuPolicy( Qt::CustomContextMenu );
    d->view->setEditTriggers( QAbstractItemView::NoEditTriggers );
    connect( d->view, SIGNAL( customContextMenuRequested( const QPoint& ) ), SLOT( showContextMenuRequested( const QPoint& ) ) );
    connect( d->view->selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), SLOT( selectionChanged( QItemSelection, QItemSelection ) ) );

    d->model->setProject( GluonEngine::Game::instance()->gameProject() );
    connect( GluonEngine::Game::instance(), SIGNAL( currentProjectChanged( GluonEngine::GameProject* ) ), SLOT( currentProjectChanged( GluonEngine::GameProject* ) ) );
    connect( d->view, SIGNAL( doubleClicked( QModelIndex ) ), SLOT( activated( QModelIndex ) ) );

    QWidget* widget = new QWidget( this );
    QVBoxLayout* layout = new QVBoxLayout();
    layout->setContentsMargins( 0, 0, 0, 0 );
    layout->setSpacing( 0 );
    widget->setLayout( layout );

    d->toolBar = new KToolBar( widget );
    d->toolBar->setIconDimensions( 16 );

    d->newMenu = new QMenu(i18n("New"), d->toolBar);

    QToolButton* menuButton = new QToolButton(d->toolBar);
    menuButton->setIcon(KIcon("document-new"));
    menuButton->setText(i18n("Add..."));
    menuButton->setPopupMode(QToolButton::InstantPopup);
    menuButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    menuButton->setMenu(d->newMenu);

    d->toolBar->insertWidget(0, menuButton);

    d->newMenu->addAction( KIcon( "folder" ), i18n( "New Folder" ), this, SLOT( newSubMenuTriggered() ) );
    d->newMenu->addAction( KIcon( "document-new" ), i18n( "New Scene" ), ObjectManager::instance(), SLOT( createNewScene() ) );
    d->newMenu->addSeparator();

    // Run through all the templates and add an action for each...
    foreach( const GluonEngine::AssetTemplate * item, d->assetTemplates )
    {
        QAction* action = d->newMenu->addAction( i18n( "New %1" ).arg( item->name ), this, SLOT( newAssetTriggered() ));
        action->setProperty( "newAssetClassname", QString( item->parent()->metaObject()->className() ) );
        action->setProperty( "newAssetName", item->name );
        action->setProperty( "newAssetPluginname", item->pluginname );
        action->setProperty( "newAssetFilename", item->filename );
    }

    d->newMenu->addSeparator();
    d->newMenu->addAction( KIcon("document-import"), i18n("Import Assets..."), this, SLOT(importAssetsTriggered()) );

    d->toolBar->addAction( KIcon( "edit-delete" ), i18nc( "Delete selected object from project", "Delete" ), this, SLOT( deleteActionTriggered() ) );

    layout->addWidget( d->toolBar );
    layout->addWidget( d->view );

    setWidget( widget );
}

ProjectDock::~ProjectDock()
{
    delete d;
}

void ProjectDock::activated( QModelIndex index )
{
    DEBUG_FUNC_NAME
    if( !index.isValid() )
    {
        return;
    }

    QObject* obj = static_cast<QObject*>( index.internalPointer() );
    if( !obj )
    {
        return;
    }

    GluonEngine::Scene* scene = qobject_cast<GluonEngine::Scene*>( obj );
    GluonEngine::Asset* asset = qobject_cast<GluonEngine::Asset*>( obj );
    // Scene's a special asset, so we check for that first
    if( scene )
    {
        if( GluonEngine::Game::instance()->currentScene() != scene )
        {
            GluonEngine::Game::instance()->setCurrentScene( scene );
            GluonEngine::Game::instance()->initializeAll();
            GluonEngine::Game::instance()->drawAll();
        }
    }
    else if( asset )
    {
        FileManager::instance()->openAsset( asset );
    }
}

void ProjectDock::selectionChanged( const QItemSelection& selected, const QItemSelection& /* deselected */ )
{
    SelectionManager::SelectionList selection;
    foreach( const QItemSelectionRange & range, selected )
    {
        foreach( const QModelIndex & index, range.indexes() )
        {
            GluonCore::GluonObject* obj = static_cast<GluonCore::GluonObject*>( index.internalPointer() );
            selection.append( obj );
        }
    }
    SelectionManager::instance()->setSelection( selection );
}

void ProjectDock::currentProjectChanged( GluonEngine::GameProject* project )
{
    d->model->setProject( project );

    if( !d->toolBar->isEnabled() )
        d->toolBar->setEnabled( true );

    d->view->expandAll();
}

void ProjectDock::showContextMenuRequested( const QPoint& pos )
{
    QModelIndex index = d->view->indexAt( pos );
    if( !index.isValid() )
        index = d->model->index( 0, 0 );

    QMenu menu( static_cast<GluonCore::GluonObject*>( index.internalPointer() )->name(), this );
    d->menuForObject(index, &menu);
    menu.exec( d->view->mapToGlobal( pos ) );
    connect( &menu, SIGNAL( aboutToHide() ), SLOT( contextMenuHiding() ) );
}

void ProjectDock::contextMenuHiding()
{
    d->currentContextIndex = QModelIndex();
}

void ProjectDock::deleteActionTriggered()
{
    DEBUG_BLOCK
    if( !d->currentContextIndex.isValid() )
        d->currentContextIndex = d->view->selectionModel()->currentIndex();

    if( !d->currentContextIndex.isValid() )
        return;

    GluonCore::GluonObject* object = static_cast<GluonCore::GluonObject*>( d->currentContextIndex.internalPointer() );

    GluonCore::ReferenceCounter* counter = dynamic_cast<GluonCore::ReferenceCounter*>(object);
    if(counter && counter->count() > 0)
    {
        QString objs = i18ncp("For shortening of the dialog text", "%1 other object", "%1 other objects", counter->count());
        KMessageBox::error(this, i18n("The object %1 is currently referenced by %2. Please remove these references before deleting the object.", object->name(), objs), i18n("Object in use"));
        return;
    }

    DEBUG_TEXT( QString( "Requested deletion of %1" ).arg( object->fullyQualifiedName() ) );
    if( KMessageBox::questionYesNo( this, i18n( "Are you sure you wish to delete %1?\nThis will delete the item and all its children!" ).arg( object->name() ), i18n( "Really Delete?" ) ) == KMessageBox::Yes )
    {
		GluonEngine::Asset* asset = qobject_cast<GluonEngine::Asset*>( object );
		if( asset )
        	ObjectManager::instance()->assetDeleted( asset );

        d->view->selectionModel()->select( d->currentContextIndex.parent(), QItemSelectionModel::ClearAndSelect );
        d->model->removeRows( d->currentContextIndex.row(), 1, d->currentContextIndex.parent() );

        d->currentContextIndex = QModelIndex();
    }
}

void ProjectDock::newSubMenuTriggered()
{
    if( !d->currentContextIndex.isValid() )
        d->currentContextIndex = d->model->index(0, 0);

    QModelIndex newFolder = d->model->addChild( new GluonCore::GluonObject( i18n("New Folder") ), d->currentContextIndex );
    d->view->edit(newFolder);
}

void GluonCreator::ProjectDock::newAssetTriggered()
{
    if( !d->currentContextIndex.isValid() )
        d->currentContextIndex = d->model->index(0, 0);

    QAction* menuItem = qobject_cast< QAction* >( QObject::sender() );
    if( menuItem )
    {
        QString templateFilename = QString( "gluon/templates/%1/%2" ).arg( menuItem->property( "newAssetPluginname" ).toString() ).arg( menuItem->property( "newAssetFilename" ).toString() );
        QString fileName = GluonCore::Global::dataDirectory() + '/' + templateFilename;
        if( fileName.isEmpty() )
        {
            DEBUG_BLOCK
            DEBUG_TEXT( "Failed at finding the template file!" );
            return;
        }

        GluonEngine::Asset* newAsset = ObjectManager::instance()->createNewAsset(fileName, static_cast<GluonCore::GluonObject*>( d->currentContextIndex.internalPointer() ),
                                                                                menuItem->property( "newAssetClassname" ).toString(),
                                                                                menuItem->property( "newAssetName" ).toString() );

        if( newAsset )
        {
            d->model->addChild( newAsset, d->currentContextIndex );
        }
    }
}

void ProjectDock::importAssetsTriggered()
{
    ObjectManager::instance()->createAssets(KFileDialog::getOpenFileNames(), static_cast<GluonCore::GluonObject*>( d->currentContextIndex.internalPointer() ));
}
<|endoftext|>


<commit_before>#include <Context.h>

#include <cmath>
#include <iostream>

using namespace std;
using namespace canvas;

void
Context::resize(unsigned int _width, unsigned int _height) {
  getDefaultSurface().resize(_width, _height, (unsigned int)(_width * getDisplayScale()), (unsigned int)(_height * getDisplayScale()), getDefaultSurface().getFormat());
  hit_regions.clear();
}

Context &
Context::fillRect(double x, double y, double w, double h) {
  return beginPath().rect(x, y, w, h).fill();
}

Context &
Context::strokeRect(double x, double y, double w, double h) {
  return beginPath().rect(x, y, w, h).stroke();
}

Context &
Context::clearRect(double x, double y, double w, double h) {
  Path path;
  path.rect(x, y, w, h);
  Style style(Color(0.0f, 0.0f, 0.0f, 0.0f));
  renderPath(FILL, path, style, COPY);
  return *this;
}

Context &
Context::renderText(RenderMode mode, const Style & style, const std::string & text, double x, double y, Operator op) {
  if (hasNativeShadows()) {
    getDefaultSurface().renderText(mode, font, style, textBaseline, textAlign, text, x, y, lineWidth, op, getDisplayScale(), globalAlpha, shadowBlur, shadowOffsetX, shadowOffsetY, shadowColor, clipPath);
  } else {
    if (hasShadow()) {
      float b = shadowBlur, bs = shadowBlur * getDisplayScale();
      int bi = int(ceil(b));
      auto shadow = createSurface(getDefaultSurface().getLogicalWidth() + 2 * bi, getDefaultSurface().getLogicalHeight() + 2 * bi, R8);
      Style shadow_style = shadowColor;
      float shadow_alpha = shadowColor.alpha;
      shadow_style.color.alpha = 1.0f;
      shadow->renderText(mode, font, shadow_style, textBaseline, textAlign, text, x + shadowOffsetX + bi, y + shadowOffsetY + bi, lineWidth, op, getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath);
#if 1
      shadow->slowBlur(bs, bs, shadow_alpha);
#else
      shadow->blur(bs);
#endif
      getDefaultSurface().drawImage(*shadow, -bi, -bi, shadow->getActualWidth(), shadow->getActualHeight(), getDisplayScale(), 1.0f, 0.0f, 0.0f, 0.0f, shadowColor, Path());
    }
    getDefaultSurface().renderText(mode, font, style, textBaseline, textAlign, text, x, y, lineWidth, op, getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath);
  }
  return *this;
}

Context &
Context::renderPath(RenderMode mode, const Path & path, const Style & style, Operator op) {
  if (hasNativeShadows()) {
    getDefaultSurface().renderPath(mode, path, style, lineWidth, op, getDisplayScale(), globalAlpha, shadowBlur, shadowOffsetX, shadowOffsetY, shadowColor, clipPath);
  } else {
    if (hasShadow()) {
      float b = shadowBlur, bs = shadowBlur * getDisplayScale();
      float bi = int(ceil(b));
      auto shadow = createSurface(getDefaultSurface().getLogicalWidth() + 2 * bi, getDefaultSurface().getLogicalHeight() + 2 * bi, R8);
      Style shadow_style = shadowColor;
      Path tmp_path = path, tmp_clipPath = clipPath;
      tmp_path.offset(shadowOffsetX + bi, shadowOffsetY + bi);
      tmp_clipPath.offset(shadowOffsetX + bi, shadowOffsetY + bi);

      shadow->renderPath(mode, tmp_path, shadow_style, lineWidth, op, getDisplayScale(), globalAlpha, 0, 0, 0, shadowColor, tmp_clipPath);
#if 1
      shadow->slowBlur(bs, bs);
#else
      shadow->blur(bs);
#endif
      getDefaultSurface().drawImage(*shadow, -bi, -bi, shadow->getActualWidth(), shadow->getActualHeight(), getDisplayScale(), 1.0f, 0.0f, 0.0f, 0.0f, shadowColor, Path(), false);
    }
    getDefaultSurface().renderPath(mode, path, style, lineWidth, op, getDisplayScale(), globalAlpha, 0, 0, 0, shadowColor, clipPath);
  }
  return *this;
}

Context &
Context::drawImage(Surface & img, double x, double y, double w, double h) {
  if (hasNativeShadows()) {
    getDefaultSurface().drawImage(img, x * getDisplayScale(), y * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, shadowBlur, shadowOffsetX, shadowOffsetY, shadowColor, clipPath, imageSmoothingEnabled);
  } else {
    if (hasShadow()) {
      float b = shadowBlur, bs = shadowBlur * getDisplayScale();
      float bi = int(ceil(b));
      auto shadow = createSurface(getDefaultSurface().getLogicalWidth() + 2 * bi, getDefaultSurface().getLogicalHeight() + 2 * bi, R8);

      shadow->drawImage(img, (x + bi + shadowOffsetX) * getDisplayScale(), (y + bi + shadowOffsetY) * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath, imageSmoothingEnabled);
      // shadow->colorFill(shadowColor);
#if 1
      shadow->slowBlur(bs, bs);
#else
      shadow->blur(bs);
#endif

      getDefaultSurface().drawImage(*shadow, -bi, -bi, shadow->getActualWidth(), shadow->getActualHeight(), getDisplayScale(), 1.0f, 0.0f, 0.0f, 0.0f, shadowColor, Path());
    }
    getDefaultSurface().drawImage(img, x * getDisplayScale(), y * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath, imageSmoothingEnabled);
  }
  return *this;
}

Context &
Context::drawImage(const Image & img, double x, double y, double w, double h) {
  if (hasNativeShadows()) {
    getDefaultSurface().drawImage(img, x * getDisplayScale(), y * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, shadowBlur, shadowOffsetX, shadowOffsetY, shadowColor, clipPath, imageSmoothingEnabled);
  } else {
    if (hasShadow()) {
      float b = shadowBlur, bs = shadowBlur * getDisplayScale();
      float bi = int(ceil(b));
      auto shadow = createSurface(getDefaultSurface().getLogicalWidth() + 2 * bi, getDefaultSurface().getLogicalHeight() + 2 * bi, R8);

      shadow->drawImage(img, (x + bi + shadowOffsetX) * getDisplayScale(), (y + bi + shadowOffsetY) * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath, imageSmoothingEnabled);
      // shadow->colorFill(shadowColor);
#if 1
      shadow->slowBlur(bs, bs);
#else
      shadow->blur(bs);
#endif

      getDefaultSurface().drawImage(*shadow, -bi, -bi, shadow->getActualWidth(), shadow->getActualHeight(), getDisplayScale(), 1.0f, 0.0f, 0.0f, 0.0f, shadowColor, Path(), false);
    }
    getDefaultSurface().drawImage(img, x * getDisplayScale(), y * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath, imageSmoothingEnabled);
  }
  return *this;
}

Context &
Context::save() {
  restore_stack.push_back(*this);
  return *this;
}

Context &
Context::restore() {
  if (!restore_stack.empty()) {
    *this = restore_stack.back();
    restore_stack.pop_back();
  }
  return *this;
}
<commit_msg>fix Style constructors<commit_after>#include <Context.h>

#include <cmath>
#include <iostream>

using namespace std;
using namespace canvas;

void
Context::resize(unsigned int _width, unsigned int _height) {
  getDefaultSurface().resize(_width, _height, (unsigned int)(_width * getDisplayScale()), (unsigned int)(_height * getDisplayScale()), getDefaultSurface().getFormat());
  hit_regions.clear();
}

Context &
Context::fillRect(double x, double y, double w, double h) {
  return beginPath().rect(x, y, w, h).fill();
}

Context &
Context::strokeRect(double x, double y, double w, double h) {
  return beginPath().rect(x, y, w, h).stroke();
}

Context &
Context::clearRect(double x, double y, double w, double h) {
  Path path;
  path.rect(x, y, w, h);
  Style style(this);
  style = Color(0.0f, 0.0f, 0.0f, 0.0f);
  renderPath(FILL, path, style, COPY);
  return *this;
}

Context &
Context::renderText(RenderMode mode, const Style & style, const std::string & text, double x, double y, Operator op) {
  if (hasNativeShadows()) {
    getDefaultSurface().renderText(mode, font, style, textBaseline, textAlign, text, x, y, lineWidth, op, getDisplayScale(), globalAlpha, shadowBlur, shadowOffsetX, shadowOffsetY, shadowColor, clipPath);
  } else {
    if (hasShadow()) {
      float b = shadowBlur, bs = shadowBlur * getDisplayScale();
      int bi = int(ceil(b));
      auto shadow = createSurface(getDefaultSurface().getLogicalWidth() + 2 * bi, getDefaultSurface().getLogicalHeight() + 2 * bi, R8);
      Style shadow_style(this);
      shadow_style = shadowColor;
      float shadow_alpha = shadowColor.alpha;
      shadow_style.color.alpha = 1.0f;
      shadow->renderText(mode, font, shadow_style, textBaseline, textAlign, text, x + shadowOffsetX + bi, y + shadowOffsetY + bi, lineWidth, op, getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath);
#if 1
      shadow->slowBlur(bs, bs, shadow_alpha);
#else
      shadow->blur(bs);
#endif
      getDefaultSurface().drawImage(*shadow, -bi, -bi, shadow->getActualWidth(), shadow->getActualHeight(), getDisplayScale(), 1.0f, 0.0f, 0.0f, 0.0f, shadowColor, Path());
    }
    getDefaultSurface().renderText(mode, font, style, textBaseline, textAlign, text, x, y, lineWidth, op, getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath);
  }
  return *this;
}

Context &
Context::renderPath(RenderMode mode, const Path & path, const Style & style, Operator op) {
  if (hasNativeShadows()) {
    getDefaultSurface().renderPath(mode, path, style, lineWidth, op, getDisplayScale(), globalAlpha, shadowBlur, shadowOffsetX, shadowOffsetY, shadowColor, clipPath);
  } else {
    if (hasShadow()) {
      float b = shadowBlur, bs = shadowBlur * getDisplayScale();
      float bi = int(ceil(b));
      auto shadow = createSurface(getDefaultSurface().getLogicalWidth() + 2 * bi, getDefaultSurface().getLogicalHeight() + 2 * bi, R8);
      Style shadow_style(this);
      shadow_style = shadowColor;
      Path tmp_path = path, tmp_clipPath = clipPath;
      tmp_path.offset(shadowOffsetX + bi, shadowOffsetY + bi);
      tmp_clipPath.offset(shadowOffsetX + bi, shadowOffsetY + bi);

      shadow->renderPath(mode, tmp_path, shadow_style, lineWidth, op, getDisplayScale(), globalAlpha, 0, 0, 0, shadowColor, tmp_clipPath);
#if 1
      shadow->slowBlur(bs, bs);
#else
      shadow->blur(bs);
#endif
      getDefaultSurface().drawImage(*shadow, -bi, -bi, shadow->getActualWidth(), shadow->getActualHeight(), getDisplayScale(), 1.0f, 0.0f, 0.0f, 0.0f, shadowColor, Path(), false);
    }
    getDefaultSurface().renderPath(mode, path, style, lineWidth, op, getDisplayScale(), globalAlpha, 0, 0, 0, shadowColor, clipPath);
  }
  return *this;
}

Context &
Context::drawImage(Surface & img, double x, double y, double w, double h) {
  if (hasNativeShadows()) {
    getDefaultSurface().drawImage(img, x * getDisplayScale(), y * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, shadowBlur, shadowOffsetX, shadowOffsetY, shadowColor, clipPath, imageSmoothingEnabled);
  } else {
    if (hasShadow()) {
      float b = shadowBlur, bs = shadowBlur * getDisplayScale();
      float bi = int(ceil(b));
      auto shadow = createSurface(getDefaultSurface().getLogicalWidth() + 2 * bi, getDefaultSurface().getLogicalHeight() + 2 * bi, R8);

      shadow->drawImage(img, (x + bi + shadowOffsetX) * getDisplayScale(), (y + bi + shadowOffsetY) * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath, imageSmoothingEnabled);
      // shadow->colorFill(shadowColor);
#if 1
      shadow->slowBlur(bs, bs);
#else
      shadow->blur(bs);
#endif

      getDefaultSurface().drawImage(*shadow, -bi, -bi, shadow->getActualWidth(), shadow->getActualHeight(), getDisplayScale(), 1.0f, 0.0f, 0.0f, 0.0f, shadowColor, Path());
    }
    getDefaultSurface().drawImage(img, x * getDisplayScale(), y * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath, imageSmoothingEnabled);
  }
  return *this;
}

Context &
Context::drawImage(const Image & img, double x, double y, double w, double h) {
  if (hasNativeShadows()) {
    getDefaultSurface().drawImage(img, x * getDisplayScale(), y * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, shadowBlur, shadowOffsetX, shadowOffsetY, shadowColor, clipPath, imageSmoothingEnabled);
  } else {
    if (hasShadow()) {
      float b = shadowBlur, bs = shadowBlur * getDisplayScale();
      float bi = int(ceil(b));
      auto shadow = createSurface(getDefaultSurface().getLogicalWidth() + 2 * bi, getDefaultSurface().getLogicalHeight() + 2 * bi, R8);

      shadow->drawImage(img, (x + bi + shadowOffsetX) * getDisplayScale(), (y + bi + shadowOffsetY) * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath, imageSmoothingEnabled);
      // shadow->colorFill(shadowColor);
#if 1
      shadow->slowBlur(bs, bs);
#else
      shadow->blur(bs);
#endif

      getDefaultSurface().drawImage(*shadow, -bi, -bi, shadow->getActualWidth(), shadow->getActualHeight(), getDisplayScale(), 1.0f, 0.0f, 0.0f, 0.0f, shadowColor, Path(), false);
    }
    getDefaultSurface().drawImage(img, x * getDisplayScale(), y * getDisplayScale(), w * getDisplayScale(), h * getDisplayScale(), getDisplayScale(), globalAlpha, 0.0f, 0.0f, 0.0f, shadowColor, clipPath, imageSmoothingEnabled);
  }
  return *this;
}

Context &
Context::save() {
  restore_stack.push_back(*this);
  return *this;
}

Context &
Context::restore() {
  if (!restore_stack.empty()) {
    *this = restore_stack.back();
    restore_stack.pop_back();
  }
  return *this;
}
<|endoftext|>


<commit_before>#include "TMessage.h"
#include "TBenchmark.h"
#include "TSocket.h"
#include "TH2.h"
#include "TTree.h"
#include "TMemFile.h"
#include "TRandom.h"
#include "TError.h"
#include "TFileMerger.h"

#include "TServerSocket.h"
#include "TPad.h"
#include "TCanvas.h"
#include "TMonitor.h"

#include "TFileCacheWrite.h"
#include "TSystem.h"
#include "THashTable.h"

#include "TMath.h"
#include "TTimeStamp.h"

const int kIncremental = 0;
const int kReplaceImmediately = 1;
const int kReplaceWait = 2;

#include "TKey.h"
static Bool_t R__NeedInitialMerge(TDirectory *dir)
{

   if (dir==0) return kFALSE;

   TIter nextkey(dir->GetListOfKeys());
   TKey *key;
   while( (key = (TKey*)nextkey()) ) {
      TClass *cl = TClass::GetClass(key->GetClassName());
      if (cl->InheritsFrom(TDirectory::Class())) {
         TDirectory *subdir = (TDirectory *)dir->GetList()->FindObject(key->GetName());
         if (!subdir) {
            subdir = (TDirectory *)key->ReadObj();
         }
         if (R__NeedInitialMerge(subdir)) {
            return kTRUE;
         }
      } else {
         if (0 != cl->GetResetAfterMerge()) {
            return kTRUE;
         }
      }
   }
   return kFALSE;
}

static void R__DeleteObject(TDirectory *dir, Bool_t withReset)
{
   if (dir==0) return;

   TIter nextkey(dir->GetListOfKeys());
   TKey *key;
   while( (key = (TKey*)nextkey()) ) {
      TClass *cl = TClass::GetClass(key->GetClassName());
      if (cl->InheritsFrom(TDirectory::Class())) {
         TDirectory *subdir = (TDirectory *)dir->GetList()->FindObject(key->GetName());
         if (!subdir) {
            subdir = (TDirectory *)key->ReadObj();
         }
         R__DeleteObject(subdir,withReset);
      } else {
         Bool_t todelete = kFALSE;
         if (withReset) {
            todelete = (0 != cl->GetResetAfterMerge());
         } else {
            todelete = (0 ==  cl->GetResetAfterMerge());
         }
         if (todelete) {
            key->Delete();
            dir->GetListOfKeys()->Remove(key);
            delete key;
         }
      }
   }
}

static void R__MigrateKey(TDirectory *destination, TDirectory *source)
{
   if (destination==0 || source==0) return;

   TIter nextkey(source->GetListOfKeys());
   TKey *key;
   while( (key = (TKey*)nextkey()) ) {
      TClass *cl = TClass::GetClass(key->GetClassName());
      if (cl->InheritsFrom(TDirectory::Class())) {
         TDirectory *source_subdir = (TDirectory *)source->GetList()->FindObject(key->GetName());
         if (!source_subdir) {
            source_subdir = (TDirectory *)key->ReadObj();
         }
         TDirectory *destination_subdir = destination->GetDirectory(key->GetName());
         if (!destination_subdir) {
            destination_subdir = destination->mkdir(key->GetName());
         }
         R__MigrateKey(destination,source);
      } else {
         TKey *oldkey = destination->GetKey(key->GetName());
         if (oldkey) {
            oldkey->Delete();
            delete oldkey;
         }
         TKey *newkey = new TKey(destination,*key,0 /* pidoffset */); // a priori the file are from the same client ..
         destination->GetFile()->SumBuffer(newkey->GetObjlen());
         newkey->WriteFile(0);
         if (destination->GetFile()->TestBit(TFile::kWriteError)) {
            return;
         }
      }
   }
   destination->SaveSelf();
}

struct ClientInfo
{
   TFile      *fFile;      // This object does *not* own the file, it will be own by the owner of the ClientInfo.
   TString    fLocalName;
   UInt_t     fContactsCount;
   TTimeStamp fLastContact;
   Double_t   fTimeSincePrevContact;

   ClientInfo() : fFile(0), fLocalName(), fContactsCount(0), fTimeSincePrevContact(0) {}
   ClientInfo(const char *filename, UInt_t clientId) : fFile(0), fContactsCount(0), fTimeSincePrevContact(0) {
      fLocalName.Form("%s-%d-%d",filename,clientId,gSystem->GetPid());
   }

   void Set(TFile *file)
   {
      // Register the new file as coming from this client.
      if (file != fFile) {
         // We need to keep any of the keys from the previous file that
         // are not in the new file.
         if (fFile) {
            R__MigrateKey(fFile,file);
            // delete the previous memory file (if any)
            delete file;
         } else {
            fFile = file;
         }
      }
      TTimeStamp now;
      fTimeSincePrevContact = now.AsDouble() - fLastContact.AsDouble();
      fLastContact = now;
      ++fContactsCount;
   }
};

struct ParallelFileMerger : public TObject
{
   typedef std::vector<ClientInfo> ClientColl_t;

   TString       fFilename;
   TBits         fClientsContact;       //
   UInt_t        fNClientsContact;      //
   ClientColl_t  fClients;
   TTimeStamp    fLastMerge;
   TFileMerger   fMerger;

   ParallelFileMerger(const char *filename, Bool_t writeCache = kFALSE) : fFilename(filename), fNClientsContact(0), fMerger(kFALSE,kTRUE)
   {
      // Default constructor.

      fMerger.SetPrintLevel(0);
      fMerger.OutputFile(filename,"RECREATE");
      if (writeCache) new TFileCacheWrite(fMerger.GetOutputFile(),32*1024*1024);
   }

   ~ParallelFileMerger()
   {
      // Destructor.

      for(unsigned int f = 0 ; f < fClients.size(); ++f) {
         fprintf(stderr,"Client %d reported %u times\n",f,fClients[f].fContactsCount);
      }
      for( ClientColl_t::iterator iter = fClients.begin();
          iter != fClients.end();
          ++iter)
      {
         delete iter->fFile;
      }
   }

   ULong_t  Hash() const
   {
      // Return hash value for this object.
      return fFilename.Hash();
   }

   const char *GetName() const
   {
      // Return the name of the object which is the name of the output file.
      return fFilename;
   }

   Bool_t InitialMerge(TFile *input)
   {
      // Initial merge of the input to copy the resetable object (TTree) into the output
      // and remove them from the input file.

      fMerger.AddFile(input);

      Bool_t result = fMerger.PartialMerge(TFileMerger::kIncremental | TFileMerger::kResetable);

      R__DeleteObject(input,kTRUE);
      return result;
   }

   Bool_t Merge()
   {
      // Merge the current inputs into the output file.

      R__DeleteObject(fMerger.GetOutputFile(),kFALSE); // Remove object that can *not* be incrementally merge and will *not* be reset by the client code.
      for(unsigned int f = 0 ; f < fClients.size(); ++f) {
         fMerger.AddFile(fClients[f].fFile);
      }
      Bool_t result = fMerger.PartialMerge(TFileMerger::kAllIncremental);

      // Remove any 'resetable' object (like TTree) from the input file so that they will not
      // be re-merged.  Keep only the object that always need to be re-merged (Histograms).
      for(unsigned int f = 0 ; f < fClients.size(); ++f) {
         if (fClients[f].fFile) {
            R__DeleteObject(fClients[f].fFile,kTRUE);
         } else {
            // We back up the file (probably due to memory constraint)
            TFile *file = TFile::Open(fClients[f].fLocalName,"UPDATE");
            R__DeleteObject(file,kTRUE); // Remove object that can be incrementally merge and will be reset by the client code.
            file->Write();
            delete file;
         }
      }
      fLastMerge = TTimeStamp();
      fNClientsContact = 0;
      fClientsContact.Clear();

      return result;
   }

   Bool_t NeedFinalMerge()
   {
      // Return true, if there is any data that has not been merged.

      return fClientsContact.CountBits() > 0;
   }

   Bool_t NeedMerge(Float_t clientThreshold)
   {
      // Return true, if enough client have reported

      if (fClients.size()==0) {
         return kFALSE;
      }

      // Calculate average and rms of the time between the last 2 contacts.
      Double_t sum = 0;
      Double_t sum2 = 0;
      for(unsigned int c = 0 ; c < fClients.size(); ++c) {
         sum += fClients[c].fTimeSincePrevContact;
         sum2 += fClients[c].fTimeSincePrevContact*fClients[c].fTimeSincePrevContact;
      }
      Double_t avg = sum / fClients.size();
      Double_t sigma = sum2 ? TMath::Sqrt( sum2 / fClients.size() - avg*avg) : 0;
      Double_t target = avg + 2*sigma;
      TTimeStamp now;
      if ( (now.AsDouble() - fLastMerge.AsDouble()) > target) {
//         Float_t cut = clientThreshold * fClients.size();
//         if (!(fClientsContact.CountBits() > cut )) {
//            for(unsigned int c = 0 ; c < fClients.size(); ++c) {
//               fprintf(stderr,"%d:%f ",c,fClients[c].fTimeSincePrevContact);
//            }
//            fprintf(stderr,"merge:%f avg:%f target:%f\n",(now.AsDouble() - fLastMerge.AsDouble()),avg,target);
//         }
         return kTRUE;
      }
      Float_t cut = clientThreshold * fClients.size();
      return fClientsContact.CountBits() > cut  || fNClientsContact > 2*cut;
   }

   void RegisterClient(UInt_t clientId, TFile *file)
   {
      // Register that a client has sent a file.

      ++fNClientsContact;
      fClientsContact.SetBitNumber(clientId);
      if (fClients.size() < clientId+1) {
         fClients.push_back( ClientInfo(fFilename,clientId) );
      }
      fClients[clientId].Set(file);
   }

   ClassDef(ParallelFileMerger,0);
};

void parallelMergeServer(bool cache = false) {
   // This script shows how to make a simple iterative server that
   // can accept connections while handling currently open connections.
   // Compare this script to hserv.C that blocks on accept.
   // In this script a server socket is created and added to a monitor.
   // A monitor object is used to monitor connection requests on
   // the server socket. After accepting the connection
   // the new socket is added to the monitor and immediately ready
   // for use. Once two connections are accepted the server socket
   // is removed from the monitor and closed. The monitor continues
   // monitoring the sockets.
   //
   // To run this demo do the following:
   //   - Open three windows
   //   - Start ROOT in all three windows
   //   - Execute in the first window: .x hserv2.C
   //   - Execute in the second and third windows: .x hclient.C
   //Author: Fons Rademakers

   // Open a server socket looking for connections on a named service or
   // on a specified port.
   //TServerSocket *ss = new TServerSocket("rootserv", kTRUE);
   TServerSocket *ss = new TServerSocket(1095, kTRUE);
   if (!ss->IsValid()) {
      return;
   }

   TMonitor *mon = new TMonitor;

   mon->Add(ss);

   UInt_t clientCount = 0;
   UInt_t clientIndex = 0;

   THashTable mergers;

   enum StatusKind {
      kStartConnection = 0,
      kProtocol = 1,

      kProtocolVersion = 1
   };

   printf("fastMergeServerHist ready to accept connections\n");
   while (1) {
      TMessage *mess;
      TSocket  *s;

      // NOTE: this needs to be update to handle the case where the client
      // dies.
      s = mon->Select();

      if (s->IsA() == TServerSocket::Class()) {
         if (clientCount > 100) {
            printf("only accept 100 clients connections\n");
            mon->Remove(ss);
            ss->Close();
         } else {
            TSocket *client = ((TServerSocket *)s)->Accept();
            client->Send(clientIndex, kStartConnection);
            client->Send(kProtocolVersion, kProtocol);
            ++clientCount;
            ++clientIndex;
            mon->Add(client);
            printf("Accept %d connections\n",clientCount);
         }
         continue;
      }

      s->Recv(mess);

      if (mess==0) {
         Error("fastMergeServer","The client did not send a message\n");
      } else if (mess->What() == kMESS_STRING) {
         char str[64];
         mess->ReadString(str, 64);
         printf("Client %d: %s\n", clientCount, str);
         mon->Remove(s);
         printf("Client %d: bytes recv = %d, bytes sent = %d\n", clientCount, s->GetBytesRecv(),
                s->GetBytesSent());
         s->Close();
         --clientCount;
         if (mon->GetActive() == 0 || clientCount == 0) {
            printf("No more active clients... stopping\n");
            break;
         }
      } else if (mess->What() == kMESS_ANY) {

         Long64_t length;
         TString filename;
         Int_t clientId;
         mess->ReadInt(clientId);
         mess->ReadTString(filename);
         mess->ReadLong64(length); // '*mess >> length;' is broken in CINT for Long64_t.

         // Info("fastMergeServerHist","Received input from client %d for %s",clientId,filename.Data());

         TMemFile *transient = new TMemFile(filename,mess->Buffer() + mess->Length(),length,"UPDATE"); // UPDATE because we need to remove the TTree after merging them.
         mess->SetBufferOffset(mess->Length()+length);

         const Float_t clientThreshold = 0.75; // control how often the histogram are merged.  Here as soon as half the clients have reported.

         ParallelFileMerger *info = (ParallelFileMerger*)mergers.FindObject(filename);
         if (!info) {
            info = new ParallelFileMerger(filename,cache);
            mergers.Add(info);
         }

         if (R__NeedInitialMerge(transient)) {
            info->InitialMerge(transient);
         }
         info->RegisterClient(clientId,transient);
         if (info->NeedMerge(clientThreshold)) {
            // Enough clients reported.
            Info("fastMergeServerHist","Merging input from %ld clients (%d)",info->fClients.size(),clientId);
            info->Merge();
         }
         transient = 0;
      } else if (mess->What() == kMESS_OBJECT) {
         printf("got object of class: %s\n", mess->GetClass()->GetName());
      } else {
         printf("*** Unexpected message ***\n");
      }

      delete mess;
   }

   TIter next(&mergers);
   ParallelFileMerger *info;
   while ( (info = (ParallelFileMerger*)next()) ) {
      if (info->NeedFinalMerge())
      {
         info->Merge();
      }
   }

   mergers.Delete();
   delete mon;
   delete ss;
}
<commit_msg>Set the server socket backlog to 100<commit_after>#include "TMessage.h"
#include "TBenchmark.h"
#include "TSocket.h"
#include "TH2.h"
#include "TTree.h"
#include "TMemFile.h"
#include "TRandom.h"
#include "TError.h"
#include "TFileMerger.h"

#include "TServerSocket.h"
#include "TPad.h"
#include "TCanvas.h"
#include "TMonitor.h"

#include "TFileCacheWrite.h"
#include "TSystem.h"
#include "THashTable.h"

#include "TMath.h"
#include "TTimeStamp.h"

const int kIncremental = 0;
const int kReplaceImmediately = 1;
const int kReplaceWait = 2;

#include "TKey.h"
static Bool_t R__NeedInitialMerge(TDirectory *dir)
{

   if (dir==0) return kFALSE;

   TIter nextkey(dir->GetListOfKeys());
   TKey *key;
   while( (key = (TKey*)nextkey()) ) {
      TClass *cl = TClass::GetClass(key->GetClassName());
      if (cl->InheritsFrom(TDirectory::Class())) {
         TDirectory *subdir = (TDirectory *)dir->GetList()->FindObject(key->GetName());
         if (!subdir) {
            subdir = (TDirectory *)key->ReadObj();
         }
         if (R__NeedInitialMerge(subdir)) {
            return kTRUE;
         }
      } else {
         if (0 != cl->GetResetAfterMerge()) {
            return kTRUE;
         }
      }
   }
   return kFALSE;
}

static void R__DeleteObject(TDirectory *dir, Bool_t withReset)
{
   if (dir==0) return;

   TIter nextkey(dir->GetListOfKeys());
   TKey *key;
   while( (key = (TKey*)nextkey()) ) {
      TClass *cl = TClass::GetClass(key->GetClassName());
      if (cl->InheritsFrom(TDirectory::Class())) {
         TDirectory *subdir = (TDirectory *)dir->GetList()->FindObject(key->GetName());
         if (!subdir) {
            subdir = (TDirectory *)key->ReadObj();
         }
         R__DeleteObject(subdir,withReset);
      } else {
         Bool_t todelete = kFALSE;
         if (withReset) {
            todelete = (0 != cl->GetResetAfterMerge());
         } else {
            todelete = (0 ==  cl->GetResetAfterMerge());
         }
         if (todelete) {
            key->Delete();
            dir->GetListOfKeys()->Remove(key);
            delete key;
         }
      }
   }
}

static void R__MigrateKey(TDirectory *destination, TDirectory *source)
{
   if (destination==0 || source==0) return;

   TIter nextkey(source->GetListOfKeys());
   TKey *key;
   while( (key = (TKey*)nextkey()) ) {
      TClass *cl = TClass::GetClass(key->GetClassName());
      if (cl->InheritsFrom(TDirectory::Class())) {
         TDirectory *source_subdir = (TDirectory *)source->GetList()->FindObject(key->GetName());
         if (!source_subdir) {
            source_subdir = (TDirectory *)key->ReadObj();
         }
         TDirectory *destination_subdir = destination->GetDirectory(key->GetName());
         if (!destination_subdir) {
            destination_subdir = destination->mkdir(key->GetName());
         }
         R__MigrateKey(destination,source);
      } else {
         TKey *oldkey = destination->GetKey(key->GetName());
         if (oldkey) {
            oldkey->Delete();
            delete oldkey;
         }
         TKey *newkey = new TKey(destination,*key,0 /* pidoffset */); // a priori the file are from the same client ..
         destination->GetFile()->SumBuffer(newkey->GetObjlen());
         newkey->WriteFile(0);
         if (destination->GetFile()->TestBit(TFile::kWriteError)) {
            return;
         }
      }
   }
   destination->SaveSelf();
}

struct ClientInfo
{
   TFile      *fFile;      // This object does *not* own the file, it will be own by the owner of the ClientInfo.
   TString    fLocalName;
   UInt_t     fContactsCount;
   TTimeStamp fLastContact;
   Double_t   fTimeSincePrevContact;

   ClientInfo() : fFile(0), fLocalName(), fContactsCount(0), fTimeSincePrevContact(0) {}
   ClientInfo(const char *filename, UInt_t clientId) : fFile(0), fContactsCount(0), fTimeSincePrevContact(0) {
      fLocalName.Form("%s-%d-%d",filename,clientId,gSystem->GetPid());
   }

   void Set(TFile *file)
   {
      // Register the new file as coming from this client.
      if (file != fFile) {
         // We need to keep any of the keys from the previous file that
         // are not in the new file.
         if (fFile) {
            R__MigrateKey(fFile,file);
            // delete the previous memory file (if any)
            delete file;
         } else {
            fFile = file;
         }
      }
      TTimeStamp now;
      fTimeSincePrevContact = now.AsDouble() - fLastContact.AsDouble();
      fLastContact = now;
      ++fContactsCount;
   }
};

struct ParallelFileMerger : public TObject
{
   typedef std::vector<ClientInfo> ClientColl_t;

   TString       fFilename;
   TBits         fClientsContact;       //
   UInt_t        fNClientsContact;      //
   ClientColl_t  fClients;
   TTimeStamp    fLastMerge;
   TFileMerger   fMerger;

   ParallelFileMerger(const char *filename, Bool_t writeCache = kFALSE) : fFilename(filename), fNClientsContact(0), fMerger(kFALSE,kTRUE)
   {
      // Default constructor.

      fMerger.SetPrintLevel(0);
      fMerger.OutputFile(filename,"RECREATE");
      if (writeCache) new TFileCacheWrite(fMerger.GetOutputFile(),32*1024*1024);
   }

   ~ParallelFileMerger()
   {
      // Destructor.

      for(unsigned int f = 0 ; f < fClients.size(); ++f) {
         fprintf(stderr,"Client %d reported %u times\n",f,fClients[f].fContactsCount);
      }
      for( ClientColl_t::iterator iter = fClients.begin();
          iter != fClients.end();
          ++iter)
      {
         delete iter->fFile;
      }
   }

   ULong_t  Hash() const
   {
      // Return hash value for this object.
      return fFilename.Hash();
   }

   const char *GetName() const
   {
      // Return the name of the object which is the name of the output file.
      return fFilename;
   }

   Bool_t InitialMerge(TFile *input)
   {
      // Initial merge of the input to copy the resetable object (TTree) into the output
      // and remove them from the input file.

      fMerger.AddFile(input);

      Bool_t result = fMerger.PartialMerge(TFileMerger::kIncremental | TFileMerger::kResetable);

      R__DeleteObject(input,kTRUE);
      return result;
   }

   Bool_t Merge()
   {
      // Merge the current inputs into the output file.

      R__DeleteObject(fMerger.GetOutputFile(),kFALSE); // Remove object that can *not* be incrementally merge and will *not* be reset by the client code.
      for(unsigned int f = 0 ; f < fClients.size(); ++f) {
         fMerger.AddFile(fClients[f].fFile);
      }
      Bool_t result = fMerger.PartialMerge(TFileMerger::kAllIncremental);

      // Remove any 'resetable' object (like TTree) from the input file so that they will not
      // be re-merged.  Keep only the object that always need to be re-merged (Histograms).
      for(unsigned int f = 0 ; f < fClients.size(); ++f) {
         if (fClients[f].fFile) {
            R__DeleteObject(fClients[f].fFile,kTRUE);
         } else {
            // We back up the file (probably due to memory constraint)
            TFile *file = TFile::Open(fClients[f].fLocalName,"UPDATE");
            R__DeleteObject(file,kTRUE); // Remove object that can be incrementally merge and will be reset by the client code.
            file->Write();
            delete file;
         }
      }
      fLastMerge = TTimeStamp();
      fNClientsContact = 0;
      fClientsContact.Clear();

      return result;
   }

   Bool_t NeedFinalMerge()
   {
      // Return true, if there is any data that has not been merged.

      return fClientsContact.CountBits() > 0;
   }

   Bool_t NeedMerge(Float_t clientThreshold)
   {
      // Return true, if enough client have reported

      if (fClients.size()==0) {
         return kFALSE;
      }

      // Calculate average and rms of the time between the last 2 contacts.
      Double_t sum = 0;
      Double_t sum2 = 0;
      for(unsigned int c = 0 ; c < fClients.size(); ++c) {
         sum += fClients[c].fTimeSincePrevContact;
         sum2 += fClients[c].fTimeSincePrevContact*fClients[c].fTimeSincePrevContact;
      }
      Double_t avg = sum / fClients.size();
      Double_t sigma = sum2 ? TMath::Sqrt( sum2 / fClients.size() - avg*avg) : 0;
      Double_t target = avg + 2*sigma;
      TTimeStamp now;
      if ( (now.AsDouble() - fLastMerge.AsDouble()) > target) {
//         Float_t cut = clientThreshold * fClients.size();
//         if (!(fClientsContact.CountBits() > cut )) {
//            for(unsigned int c = 0 ; c < fClients.size(); ++c) {
//               fprintf(stderr,"%d:%f ",c,fClients[c].fTimeSincePrevContact);
//            }
//            fprintf(stderr,"merge:%f avg:%f target:%f\n",(now.AsDouble() - fLastMerge.AsDouble()),avg,target);
//         }
         return kTRUE;
      }
      Float_t cut = clientThreshold * fClients.size();
      return fClientsContact.CountBits() > cut  || fNClientsContact > 2*cut;
   }

   void RegisterClient(UInt_t clientId, TFile *file)
   {
      // Register that a client has sent a file.

      ++fNClientsContact;
      fClientsContact.SetBitNumber(clientId);
      if (fClients.size() < clientId+1) {
         fClients.push_back( ClientInfo(fFilename,clientId) );
      }
      fClients[clientId].Set(file);
   }

   ClassDef(ParallelFileMerger,0);
};

void parallelMergeServer(bool cache = false) {
   // This script shows how to make a simple iterative server that
   // can accept connections while handling currently open connections.
   // Compare this script to hserv.C that blocks on accept.
   // In this script a server socket is created and added to a monitor.
   // A monitor object is used to monitor connection requests on
   // the server socket. After accepting the connection
   // the new socket is added to the monitor and immediately ready
   // for use. Once two connections are accepted the server socket
   // is removed from the monitor and closed. The monitor continues
   // monitoring the sockets.
   //
   // To run this demo do the following:
   //   - Open three windows
   //   - Start ROOT in all three windows
   //   - Execute in the first window: .x hserv2.C
   //   - Execute in the second and third windows: .x hclient.C
   //Author: Fons Rademakers

   // Open a server socket looking for connections on a named service or
   // on a specified port.
   //TServerSocket *ss = new TServerSocket("rootserv", kTRUE);
   TServerSocket *ss = new TServerSocket(1095, kTRUE, 100);
   if (!ss->IsValid()) {
      return;
   }

   TMonitor *mon = new TMonitor;

   mon->Add(ss);

   UInt_t clientCount = 0;
   UInt_t clientIndex = 0;

   THashTable mergers;

   enum StatusKind {
      kStartConnection = 0,
      kProtocol = 1,

      kProtocolVersion = 1
   };

   printf("fastMergeServerHist ready to accept connections\n");
   while (1) {
      TMessage *mess;
      TSocket  *s;

      // NOTE: this needs to be update to handle the case where the client
      // dies.
      s = mon->Select();

      if (s->IsA() == TServerSocket::Class()) {
         if (clientCount > 100) {
            printf("only accept 100 clients connections\n");
            mon->Remove(ss);
            ss->Close();
         } else {
            TSocket *client = ((TServerSocket *)s)->Accept();
            client->Send(clientIndex, kStartConnection);
            client->Send(kProtocolVersion, kProtocol);
            ++clientCount;
            ++clientIndex;
            mon->Add(client);
            printf("Accept %d connections\n",clientCount);
         }
         continue;
      }

      s->Recv(mess);

      if (mess==0) {
         Error("fastMergeServer","The client did not send a message\n");
      } else if (mess->What() == kMESS_STRING) {
         char str[64];
         mess->ReadString(str, 64);
         printf("Client %d: %s\n", clientCount, str);
         mon->Remove(s);
         printf("Client %d: bytes recv = %d, bytes sent = %d\n", clientCount, s->GetBytesRecv(),
                s->GetBytesSent());
         s->Close();
         --clientCount;
         if (mon->GetActive() == 0 || clientCount == 0) {
            printf("No more active clients... stopping\n");
            break;
         }
      } else if (mess->What() == kMESS_ANY) {

         Long64_t length;
         TString filename;
         Int_t clientId;
         mess->ReadInt(clientId);
         mess->ReadTString(filename);
         mess->ReadLong64(length); // '*mess >> length;' is broken in CINT for Long64_t.

         // Info("fastMergeServerHist","Received input from client %d for %s",clientId,filename.Data());

         TMemFile *transient = new TMemFile(filename,mess->Buffer() + mess->Length(),length,"UPDATE"); // UPDATE because we need to remove the TTree after merging them.
         mess->SetBufferOffset(mess->Length()+length);

         const Float_t clientThreshold = 0.75; // control how often the histogram are merged.  Here as soon as half the clients have reported.

         ParallelFileMerger *info = (ParallelFileMerger*)mergers.FindObject(filename);
         if (!info) {
            info = new ParallelFileMerger(filename,cache);
            mergers.Add(info);
         }

         if (R__NeedInitialMerge(transient)) {
            info->InitialMerge(transient);
         }
         info->RegisterClient(clientId,transient);
         if (info->NeedMerge(clientThreshold)) {
            // Enough clients reported.
            Info("fastMergeServerHist","Merging input from %ld clients (%d)",info->fClients.size(),clientId);
            info->Merge();
         }
         transient = 0;
      } else if (mess->What() == kMESS_OBJECT) {
         printf("got object of class: %s\n", mess->GetClass()->GetName());
      } else {
         printf("*** Unexpected message ***\n");
      }

      delete mess;
   }

   TIter next(&mergers);
   ParallelFileMerger *info;
   while ( (info = (ParallelFileMerger*)next()) ) {
      if (info->NeedFinalMerge())
      {
         info->Merge();
      }
   }

   mergers.Delete();
   delete mon;
   delete ss;
}
<|endoftext|>


<commit_before>// utility functions
//
#include "Utility.h"
#include "Sum.h"
#include "Product.h"

using namespace std;

short qualityChar2ShortInt(char c) {
    return static_cast<short>(c) - 33;
}

long double phred2ln(int qual) {
    return M_LN10 * qual * -.1;
}

int ln2phred(long double prob) {
    return -10 * M_LOG10E * prob;
}

long double phred2float(int qual) {
    return pow(10,qual * -.1);
}

int float2phred(long double prob) {
    if (prob == 1)
        return 99;
    return std::min(-10 * (long double) log10(prob), (long double) 99);
}

long double powln(long double m, int n) {
    long double r = 0;
    for (; n > 0; --n) {
        r += m;
    }
    return r;
}

// TODO do the following in phred (log) space for perf boost

// the probability that we have a completely true vector of qualities
short jointQuality(const std::vector<short>& quals) {
    std::vector<long double> probs;
    for (int i = 0; i<quals.size(); ++i) {
        probs.push_back(phred2float(quals[i]));
    }
    // product of probability we don't have a true event for each element
    long double prod = 1 - probs.front();
    for (int i = 1; i<probs.size(); ++i) {
        prod *= 1 - probs.at(i);
    }
    // and then invert it again to get probability of an event
    return float2phred(1 - prod);
}

short jointQuality(const std::string& qualstr) {

    std::vector<short> quals;
    for (int i=0; i<qualstr.size(); i++)
        quals.push_back(qualityChar2ShortInt(qualstr.at(i)));

    return jointQuality(quals);

}

short averageQuality(const std::string& qualstr) {

    long double q = 0;
    for (int i=0; i<qualstr.size(); i++)
        q += phred2float(qualityChar2ShortInt(qualstr.at(i)));
    return float2phred(q / qualstr.size());

}

bool stringInVector(string item, vector<string> items) {
    for (vector<string>::iterator i = items.begin(); i != items.end(); ++i) {
        if (item == *i) {
            return true;
        }
    }
    return false;
}

// from Function-Math.cpp

long double gammaln(
		     long double x
		     ) {

  long double cofactors[] = { 76.18009173,
                              -86.50532033,
                              24.01409822,
                              -1.231739516,
                              0.120858003E-2,
                              -0.536382E-5 };

  long double x1 = x - 1.0;
  long double tmp = x1 + 5.5;
  tmp -= (x1 + 0.5) * log(tmp);
  long double ser = 1.0;
  for (int j=0; j<=5; j++) {
    x1 += 1.0;
    ser += cofactors[j]/x1;
  }
  long double y =  (-1.0 * tmp + log(2.50662827465 * ser));

  return y;
}

long double factorial(
		      int n
		      ) {
  if (n < 0) {
    return (long double)0.0;
  }
  else if (n == 0) {
    return (long double)1.0;
  }
  else {
    return exp(gammaln(n + 1.0));
  }
}

long double factorialln(
			int n
			) {
  if (n < 0) {
    return (long double)-1.0;
  }
  else if (n == 0) {
    return (long double)0.0;
  }
  else {
    return gammaln(n + 1.0);
  }
}

long double cofactor(
		     int n,
		     int i
		     ) {
  if ((n < 0) || (i < 0) || (n < i)) {
    return (long double)0.0;
  }
  else if (n == i) {
    return (long double)1.0;
  }
  else {
    return exp(gammaln(n + 1.0) - gammaln(i + 1.0) - gammaln(n-i + 1.0));
  }
}

long double cofactorln(
		       int n,
		       int i
		       ) {
  if ((n < 0) || (i < 0) || (n < i)) {
    return (long double)-1.0;
  }
  else if (n == i) {
    return (long double)0.0;
  }
  else {
    return gammaln(n + 1.0) - gammaln(i + 1.0) - gammaln(n-i + 1.0);
  }
}

long double logsumexp(const vector<long double>& lnv) {
    long double maxAbs, minN, maxN, c;
    vector<long double>::const_iterator i = lnv.begin();
    long double n = *i;
    maxAbs = n; maxN = n; minN = n;
    ++i;
    for (; i != lnv.end(); ++i) {
        n = *i;
        if (n > maxN)
            maxN = n;
        if (fabs(n) > maxAbs)
            maxAbs = fabs(n);
        if (n < minN)
            minN = n;
    }
    if (maxAbs > maxN) {
        c = minN;
    } else {
        c = maxN;
    }
    long double sum = 0;
    for (vector<long double>::const_iterator i = lnv.begin(); i != lnv.end(); ++i) {
        sum += exp(*i - c);
    }
    return c + log(sum);
}

long double betaln(const vector<long double>& alphas) {
    vector<long double> gammalnAlphas;
    gammalnAlphas.resize(alphas.size());
    transform(alphas.begin(), alphas.end(), gammalnAlphas.begin(), gammaln);
    return sum(gammalnAlphas) - gammaln(sum(alphas));
}

long double beta(const vector<long double>& alphas) {
    return exp(betaln(alphas));
}
<commit_msg>int overflow guard for float2phred conversion<commit_after>// utility functions
//
#include "Utility.h"
#include "Sum.h"
#include "Product.h"

using namespace std;

short qualityChar2ShortInt(char c) {
    return static_cast<short>(c) - 33;
}

long double phred2ln(int qual) {
    return M_LN10 * qual * -.1;
}

int ln2phred(long double prob) {
    return -10 * M_LOG10E * prob;
}

long double phred2float(int qual) {
    return pow(10,qual * -.1);
}

int float2phred(long double prob) {
    if (prob == 1)
        return 99;
    int p = -10 * (long double) log10(prob);
    if (p < 0 || p > 99) // int overflow guard
        return 99;
    else
        return p;
}

long double powln(long double m, int n) {
    long double r = 0;
    for (; n > 0; --n) {
        r += m;
    }
    return r;
}

// TODO do the following in phred (log) space for perf boost

// the probability that we have a completely true vector of qualities
short jointQuality(const std::vector<short>& quals) {
    std::vector<long double> probs;
    for (int i = 0; i<quals.size(); ++i) {
        probs.push_back(phred2float(quals[i]));
    }
    // product of probability we don't have a true event for each element
    long double prod = 1 - probs.front();
    for (int i = 1; i<probs.size(); ++i) {
        prod *= 1 - probs.at(i);
    }
    // and then invert it again to get probability of an event
    return float2phred(1 - prod);
}

short jointQuality(const std::string& qualstr) {

    std::vector<short> quals;
    for (int i=0; i<qualstr.size(); i++)
        quals.push_back(qualityChar2ShortInt(qualstr.at(i)));

    return jointQuality(quals);

}

short averageQuality(const std::string& qualstr) {

    long double q = 0;
    for (int i=0; i<qualstr.size(); i++)
        q += phred2float(qualityChar2ShortInt(qualstr.at(i)));
    return float2phred(q / qualstr.size());

}

bool stringInVector(string item, vector<string> items) {
    for (vector<string>::iterator i = items.begin(); i != items.end(); ++i) {
        if (item == *i) {
            return true;
        }
    }
    return false;
}

// from Function-Math.cpp

long double gammaln(
		     long double x
		     ) {

  long double cofactors[] = { 76.18009173,
                              -86.50532033,
                              24.01409822,
                              -1.231739516,
                              0.120858003E-2,
                              -0.536382E-5 };

  long double x1 = x - 1.0;
  long double tmp = x1 + 5.5;
  tmp -= (x1 + 0.5) * log(tmp);
  long double ser = 1.0;
  for (int j=0; j<=5; j++) {
    x1 += 1.0;
    ser += cofactors[j]/x1;
  }
  long double y =  (-1.0 * tmp + log(2.50662827465 * ser));

  return y;
}

long double factorial(
		      int n
		      ) {
  if (n < 0) {
    return (long double)0.0;
  }
  else if (n == 0) {
    return (long double)1.0;
  }
  else {
    return exp(gammaln(n + 1.0));
  }
}

long double factorialln(
			int n
			) {
  if (n < 0) {
    return (long double)-1.0;
  }
  else if (n == 0) {
    return (long double)0.0;
  }
  else {
    return gammaln(n + 1.0);
  }
}

long double cofactor(
		     int n,
		     int i
		     ) {
  if ((n < 0) || (i < 0) || (n < i)) {
    return (long double)0.0;
  }
  else if (n == i) {
    return (long double)1.0;
  }
  else {
    return exp(gammaln(n + 1.0) - gammaln(i + 1.0) - gammaln(n-i + 1.0));
  }
}

long double cofactorln(
		       int n,
		       int i
		       ) {
  if ((n < 0) || (i < 0) || (n < i)) {
    return (long double)-1.0;
  }
  else if (n == i) {
    return (long double)0.0;
  }
  else {
    return gammaln(n + 1.0) - gammaln(i + 1.0) - gammaln(n-i + 1.0);
  }
}

long double logsumexp(const vector<long double>& lnv) {
    long double maxAbs, minN, maxN, c;
    vector<long double>::const_iterator i = lnv.begin();
    long double n = *i;
    maxAbs = n; maxN = n; minN = n;
    ++i;
    for (; i != lnv.end(); ++i) {
        n = *i;
        if (n > maxN)
            maxN = n;
        if (fabs(n) > maxAbs)
            maxAbs = fabs(n);
        if (n < minN)
            minN = n;
    }
    if (maxAbs > maxN) {
        c = minN;
    } else {
        c = maxN;
    }
    long double sum = 0;
    for (vector<long double>::const_iterator i = lnv.begin(); i != lnv.end(); ++i) {
        sum += exp(*i - c);
    }
    return c + log(sum);
}

long double betaln(const vector<long double>& alphas) {
    vector<long double> gammalnAlphas;
    gammalnAlphas.resize(alphas.size());
    transform(alphas.begin(), alphas.end(), gammalnAlphas.begin(), gammaln);
    return sum(gammalnAlphas) - gammaln(sum(alphas));
}

long double beta(const vector<long double>& alphas) {
    return exp(betaln(alphas));
}
<|endoftext|>


<commit_before>#include "process.hpp"
#include <cstring>
#include <TlHelp32.h>
#include <stdexcept>

Process::Data::Data(): id(0), handle(NULL) {}

namespace {

// Simple HANDLE wrapper to close it automatically from the destructor.
class Handle {
public:
  Handle() : handle(INVALID_HANDLE_VALUE) { }
  ~Handle() {
    close();
  }
  void close() {
    if (handle != INVALID_HANDLE_VALUE)
      ::CloseHandle(handle);
  }
  HANDLE detach() {
    HANDLE old_handle = handle;
    handle = nullptr;
    return old_handle;
  }
  operator HANDLE() const { return handle; }
  HANDLE* operator&() { return &handle; }
private:
  HANDLE handle;
};

//Based on the discussion thread: https://www.reddit.com/r/cpp/comments/3vpjqg/a_new_platform_independent_process_library_for_c11/cxq1wsj
std::mutex create_process_mutex;

}

//Based on the example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx.
Process::id_type Process::open(const std::string &command, const std::string &path) {
  if(open_stdin)
    stdin_fd=std::unique_ptr<fd_type>(new fd_type);
  if(read_stdout)
    stdout_fd=std::unique_ptr<fd_type>(new fd_type);
  if(read_stderr)
    stderr_fd=std::unique_ptr<fd_type>(new fd_type);

  Handle stdin_rd_p;
  Handle stdin_wr_p;
  Handle stdout_rd_p;
  Handle stdout_wr_p;
  Handle stderr_rd_p;
  Handle stderr_wr_p;

  SECURITY_ATTRIBUTES security_attributes;

  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  security_attributes.bInheritHandle = TRUE;
  security_attributes.lpSecurityDescriptor = NULL;

  std::lock_guard<std::mutex> lock(create_process_mutex);
  if(stdin_fd) {
    if (!CreatePipe(&stdin_rd_p, &stdin_wr_p, &security_attributes, 0) ||
        !SetHandleInformation(stdin_wr_p, HANDLE_FLAG_INHERIT, 0))
      return 0;
  }
  if(stdout_fd) {
    if (!CreatePipe(&stdout_rd_p, &stdout_wr_p, &security_attributes, 0) ||
        !SetHandleInformation(stdout_rd_p, HANDLE_FLAG_INHERIT, 0)) {
      return 0;
    }
  }
  if(stderr_fd) {
    if (!CreatePipe(&stderr_rd_p, &stderr_wr_p, &security_attributes, 0) ||
        !SetHandleInformation(stderr_rd_p, HANDLE_FLAG_INHERIT, 0)) {
      return 0;
    }
  }

  PROCESS_INFORMATION process_info;
  STARTUPINFO startup_info;

  ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));

  ZeroMemory(&startup_info, sizeof(STARTUPINFO));
  startup_info.cb = sizeof(STARTUPINFO);
  startup_info.hStdInput = stdin_rd_p;
  startup_info.hStdOutput = stdout_wr_p;
  startup_info.hStdError = stderr_wr_p;
  if(stdin_fd || stdout_fd || stderr_fd)
    startup_info.dwFlags |= STARTF_USESTDHANDLES;

  char* path_cstr;
  if(path=="")
    path_cstr=NULL;
  else {
    path_cstr=new char[path.size()+1];
    std::strcpy(path_cstr, path.c_str());
  }

  char* command_cstr;
#ifdef MSYS_PROCESS_USE_SH
  size_t pos=0;
  std::string sh_command=command;
  while((pos=sh_command.find('\\', pos))!=std::string::npos) {
    sh_command.replace(pos, 1, "\\\\\\\\");
    pos+=4;
  }
  pos=0;
  while((pos=sh_command.find('\"', pos))!=std::string::npos) {
    sh_command.replace(pos, 1, "\\\"");
    pos+=2;
  }
  sh_command.insert(0, "sh -c \"");
  sh_command+="\"";
  command_cstr=new char[sh_command.size()+1];
  std::strcpy(command_cstr, sh_command.c_str());
#else
  command_cstr=new char[command.size()+1];
  std::strcpy(command_cstr, command.c_str());
#endif

  BOOL bSuccess = CreateProcess(NULL, command_cstr, NULL, NULL, TRUE, 0,
                                NULL, path_cstr, &startup_info, &process_info);
  delete[] path_cstr;
  delete[] command_cstr;

  if(!bSuccess) {
    CloseHandle(process_info.hProcess);
    CloseHandle(process_info.hThread);
    return 0;
  }
  else {
    CloseHandle(process_info.hThread);
  }

  if(stdin_fd) *stdin_fd=stdin_wr_p.detach();
  if(stdout_fd) *stdout_fd=stdout_rd_p.detach();
  if(stderr_fd) *stderr_fd=stderr_rd_p.detach();

  closed=false;
  data.id=process_info.dwProcessId;
  data.handle=process_info.hProcess;
  return process_info.dwProcessId;
}

void Process::async_read() {
  if(data.id==0)
    return;
  if(stdout_fd) {
    stdout_thread=std::thread([this](){
      DWORD n;
      std::unique_ptr<char[]> buffer(new char[buffer_size]);
      for (;;) {
        BOOL bSuccess = ReadFile(*stdout_fd, static_cast<CHAR*>(buffer.get()), static_cast<DWORD>(buffer_size), &n, NULL);
        if(!bSuccess || n == 0)
          break;
        read_stdout(buffer.get(), static_cast<size_t>(n));
      }
    });
  }
  if(stderr_fd) {
    stderr_thread=std::thread([this](){
      DWORD n;
      std::unique_ptr<char[]> buffer(new char[buffer_size]);
      for (;;) {
        BOOL bSuccess = ReadFile(*stderr_fd, static_cast<CHAR*>(buffer.get()), static_cast<DWORD>(buffer_size), &n, NULL);
        if(!bSuccess || n == 0)
          break;
        read_stderr(buffer.get(), static_cast<size_t>(n));
      }
    });
  }
}

int Process::get_exit_status() {
  if(data.id==0)
    return -1;
  DWORD exit_status;
  WaitForSingleObject(data.handle, INFINITE);
  if(!GetExitCodeProcess(data.handle, &exit_status))
    exit_status=-1;
  {
    std::lock_guard<std::mutex> lock(close_mutex);
    CloseHandle(data.handle);
    closed=true;
  }
  close_fds();

  return static_cast<int>(exit_status);
}

void Process::close_fds() {
  if(stdout_thread.joinable())
    stdout_thread.join();
  if(stderr_thread.joinable())
    stderr_thread.join();

  if(stdin_fd)
    close_stdin();
  if(stdout_fd) {
    CloseHandle(*stdout_fd);
    stdout_fd.reset();
  }
  if(stderr_fd) {
    CloseHandle(*stderr_fd);
    stderr_fd.reset();
  }
}

bool Process::write(const char *bytes, size_t n) {
  if(!open_stdin)
    throw std::invalid_argument("Can't write to an unopened stdin pipe. Please set open_stdin=true when constructing the process.");

  std::lock_guard<std::mutex> lock(stdin_mutex);
  if(stdin_fd) {
    DWORD written;
    BOOL bSuccess=WriteFile(*stdin_fd, bytes, static_cast<DWORD>(n), &written, NULL);
    if(!bSuccess || written==0) {
      return false;
    }
    else {
      return true;
    }
  }
  return false;
}

void Process::close_stdin() {
  std::lock_guard<std::mutex> lock(stdin_mutex);
  if(stdin_fd) {
    CloseHandle(*stdin_fd);
    stdin_fd.reset();
  }
}

//Based on http://stackoverflow.com/a/1173396
void Process::kill(bool force) {
  std::lock_guard<std::mutex> lock(close_mutex);
  if(data.id>0 && !closed) {
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(snapshot) {
      PROCESSENTRY32 process;
      ZeroMemory(&process, sizeof(process));
      process.dwSize = sizeof(process);
      if(Process32First(snapshot, &process)) {
        do {
          if(process.th32ParentProcessID==data.id) {
            HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID);
            if(process_handle) TerminateProcess(process_handle, 2);
          }
        } while (Process32Next(snapshot, &process));
      }
    }
    TerminateProcess(data.handle, 2);
  }
}

//Based on http://stackoverflow.com/a/1173396
void Process::kill(id_type id, bool force) {
  if(id==0)
    return;
  HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if(snapshot) {
    PROCESSENTRY32 process;
    ZeroMemory(&process, sizeof(process));
    process.dwSize = sizeof(process);
    if(Process32First(snapshot, &process)) {
      do {
        if(process.th32ParentProcessID==id) {
          HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID);
          if(process_handle) TerminateProcess(process_handle, 2);
        }
      } while (Process32Next(snapshot, &process));
    }
  }
  HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
  if(process_handle) TerminateProcess(process_handle, 2);
}
<commit_msg>Restore the correct invalid handle value in Handle::detach()<commit_after>#include "process.hpp"
#include <cstring>
#include <TlHelp32.h>
#include <stdexcept>

Process::Data::Data(): id(0), handle(NULL) {}

namespace {

// Simple HANDLE wrapper to close it automatically from the destructor.
class Handle {
public:
  Handle() : handle(INVALID_HANDLE_VALUE) { }
  ~Handle() {
    close();
  }
  void close() {
    if (handle != INVALID_HANDLE_VALUE)
      ::CloseHandle(handle);
  }
  HANDLE detach() {
    HANDLE old_handle = handle;
    handle = INVALID_HANDLE_VALUE;
    return old_handle;
  }
  operator HANDLE() const { return handle; }
  HANDLE* operator&() { return &handle; }
private:
  HANDLE handle;
};

//Based on the discussion thread: https://www.reddit.com/r/cpp/comments/3vpjqg/a_new_platform_independent_process_library_for_c11/cxq1wsj
std::mutex create_process_mutex;

}

//Based on the example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx.
Process::id_type Process::open(const std::string &command, const std::string &path) {
  if(open_stdin)
    stdin_fd=std::unique_ptr<fd_type>(new fd_type);
  if(read_stdout)
    stdout_fd=std::unique_ptr<fd_type>(new fd_type);
  if(read_stderr)
    stderr_fd=std::unique_ptr<fd_type>(new fd_type);

  Handle stdin_rd_p;
  Handle stdin_wr_p;
  Handle stdout_rd_p;
  Handle stdout_wr_p;
  Handle stderr_rd_p;
  Handle stderr_wr_p;

  SECURITY_ATTRIBUTES security_attributes;

  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  security_attributes.bInheritHandle = TRUE;
  security_attributes.lpSecurityDescriptor = NULL;

  std::lock_guard<std::mutex> lock(create_process_mutex);
  if(stdin_fd) {
    if (!CreatePipe(&stdin_rd_p, &stdin_wr_p, &security_attributes, 0) ||
        !SetHandleInformation(stdin_wr_p, HANDLE_FLAG_INHERIT, 0))
      return 0;
  }
  if(stdout_fd) {
    if (!CreatePipe(&stdout_rd_p, &stdout_wr_p, &security_attributes, 0) ||
        !SetHandleInformation(stdout_rd_p, HANDLE_FLAG_INHERIT, 0)) {
      return 0;
    }
  }
  if(stderr_fd) {
    if (!CreatePipe(&stderr_rd_p, &stderr_wr_p, &security_attributes, 0) ||
        !SetHandleInformation(stderr_rd_p, HANDLE_FLAG_INHERIT, 0)) {
      return 0;
    }
  }

  PROCESS_INFORMATION process_info;
  STARTUPINFO startup_info;

  ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));

  ZeroMemory(&startup_info, sizeof(STARTUPINFO));
  startup_info.cb = sizeof(STARTUPINFO);
  startup_info.hStdInput = stdin_rd_p;
  startup_info.hStdOutput = stdout_wr_p;
  startup_info.hStdError = stderr_wr_p;
  if(stdin_fd || stdout_fd || stderr_fd)
    startup_info.dwFlags |= STARTF_USESTDHANDLES;

  char* path_cstr;
  if(path=="")
    path_cstr=NULL;
  else {
    path_cstr=new char[path.size()+1];
    std::strcpy(path_cstr, path.c_str());
  }

  char* command_cstr;
#ifdef MSYS_PROCESS_USE_SH
  size_t pos=0;
  std::string sh_command=command;
  while((pos=sh_command.find('\\', pos))!=std::string::npos) {
    sh_command.replace(pos, 1, "\\\\\\\\");
    pos+=4;
  }
  pos=0;
  while((pos=sh_command.find('\"', pos))!=std::string::npos) {
    sh_command.replace(pos, 1, "\\\"");
    pos+=2;
  }
  sh_command.insert(0, "sh -c \"");
  sh_command+="\"";
  command_cstr=new char[sh_command.size()+1];
  std::strcpy(command_cstr, sh_command.c_str());
#else
  command_cstr=new char[command.size()+1];
  std::strcpy(command_cstr, command.c_str());
#endif

  BOOL bSuccess = CreateProcess(NULL, command_cstr, NULL, NULL, TRUE, 0,
                                NULL, path_cstr, &startup_info, &process_info);
  delete[] path_cstr;
  delete[] command_cstr;

  if(!bSuccess) {
    CloseHandle(process_info.hProcess);
    CloseHandle(process_info.hThread);
    return 0;
  }
  else {
    CloseHandle(process_info.hThread);
  }

  if(stdin_fd) *stdin_fd=stdin_wr_p.detach();
  if(stdout_fd) *stdout_fd=stdout_rd_p.detach();
  if(stderr_fd) *stderr_fd=stderr_rd_p.detach();

  closed=false;
  data.id=process_info.dwProcessId;
  data.handle=process_info.hProcess;
  return process_info.dwProcessId;
}

void Process::async_read() {
  if(data.id==0)
    return;
  if(stdout_fd) {
    stdout_thread=std::thread([this](){
      DWORD n;
      std::unique_ptr<char[]> buffer(new char[buffer_size]);
      for (;;) {
        BOOL bSuccess = ReadFile(*stdout_fd, static_cast<CHAR*>(buffer.get()), static_cast<DWORD>(buffer_size), &n, NULL);
        if(!bSuccess || n == 0)
          break;
        read_stdout(buffer.get(), static_cast<size_t>(n));
      }
    });
  }
  if(stderr_fd) {
    stderr_thread=std::thread([this](){
      DWORD n;
      std::unique_ptr<char[]> buffer(new char[buffer_size]);
      for (;;) {
        BOOL bSuccess = ReadFile(*stderr_fd, static_cast<CHAR*>(buffer.get()), static_cast<DWORD>(buffer_size), &n, NULL);
        if(!bSuccess || n == 0)
          break;
        read_stderr(buffer.get(), static_cast<size_t>(n));
      }
    });
  }
}

int Process::get_exit_status() {
  if(data.id==0)
    return -1;
  DWORD exit_status;
  WaitForSingleObject(data.handle, INFINITE);
  if(!GetExitCodeProcess(data.handle, &exit_status))
    exit_status=-1;
  {
    std::lock_guard<std::mutex> lock(close_mutex);
    CloseHandle(data.handle);
    closed=true;
  }
  close_fds();

  return static_cast<int>(exit_status);
}

void Process::close_fds() {
  if(stdout_thread.joinable())
    stdout_thread.join();
  if(stderr_thread.joinable())
    stderr_thread.join();

  if(stdin_fd)
    close_stdin();
  if(stdout_fd) {
    CloseHandle(*stdout_fd);
    stdout_fd.reset();
  }
  if(stderr_fd) {
    CloseHandle(*stderr_fd);
    stderr_fd.reset();
  }
}

bool Process::write(const char *bytes, size_t n) {
  if(!open_stdin)
    throw std::invalid_argument("Can't write to an unopened stdin pipe. Please set open_stdin=true when constructing the process.");

  std::lock_guard<std::mutex> lock(stdin_mutex);
  if(stdin_fd) {
    DWORD written;
    BOOL bSuccess=WriteFile(*stdin_fd, bytes, static_cast<DWORD>(n), &written, NULL);
    if(!bSuccess || written==0) {
      return false;
    }
    else {
      return true;
    }
  }
  return false;
}

void Process::close_stdin() {
  std::lock_guard<std::mutex> lock(stdin_mutex);
  if(stdin_fd) {
    CloseHandle(*stdin_fd);
    stdin_fd.reset();
  }
}

//Based on http://stackoverflow.com/a/1173396
void Process::kill(bool force) {
  std::lock_guard<std::mutex> lock(close_mutex);
  if(data.id>0 && !closed) {
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(snapshot) {
      PROCESSENTRY32 process;
      ZeroMemory(&process, sizeof(process));
      process.dwSize = sizeof(process);
      if(Process32First(snapshot, &process)) {
        do {
          if(process.th32ParentProcessID==data.id) {
            HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID);
            if(process_handle) TerminateProcess(process_handle, 2);
          }
        } while (Process32Next(snapshot, &process));
      }
    }
    TerminateProcess(data.handle, 2);
  }
}

//Based on http://stackoverflow.com/a/1173396
void Process::kill(id_type id, bool force) {
  if(id==0)
    return;
  HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if(snapshot) {
    PROCESSENTRY32 process;
    ZeroMemory(&process, sizeof(process));
    process.dwSize = sizeof(process);
    if(Process32First(snapshot, &process)) {
      do {
        if(process.th32ParentProcessID==id) {
          HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID);
          if(process_handle) TerminateProcess(process_handle, 2);
        }
      } while (Process32Next(snapshot, &process));
    }
  }
  HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
  if(process_handle) TerminateProcess(process_handle, 2);
}
<|endoftext|>