<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: regexpmap.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 15:18:30 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _UCB_REGEXPMAP_HXX_
#define _UCB_REGEXPMAP_HXX_

#ifndef _RTL_OUSTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _SAL_TYPES_H_
#include <sal/types.h>
#endif

namespace ucb {

template< typename Val > class RegexpMap;
template< typename Val > class RegexpMapIter;

//============================================================================
template< typename Val >
class RegexpMapEntry
{
public:
    inline RegexpMapEntry(rtl::OUString const & rTheRegexp,
                          Val * pTheValue):
        m_aRegexp(rTheRegexp), m_pValue(pTheValue) {}

    rtl::OUString getRegexp() const { return m_aRegexp; }

    Val const & getValue() const { return *m_pValue; }

    Val & getValue() { return *m_pValue; }

private:
    rtl::OUString m_aRegexp;
    Val * m_pValue;
};

//============================================================================
template< typename Val > class RegexpMapIterImpl;
    // MSC doesn't like this to be a private RegexpMapConstIter member
    // class...

template< typename Val >
class RegexpMapConstIter
{
    friend class RegexpMap< Val >; // to access m_pImpl, ctor
    friend class RegexpMapIter< Val >; // to access m_pImpl, ctor

public:
    RegexpMapConstIter();

    RegexpMapConstIter(RegexpMapConstIter const & rOther);

    ~RegexpMapConstIter();

    RegexpMapConstIter & operator =(RegexpMapConstIter const & rOther);

    RegexpMapConstIter & operator ++();

    RegexpMapConstIter operator ++(int);

    RegexpMapEntry< Val > const & operator *() const;

    RegexpMapEntry< Val > const * operator ->() const;

    bool equals(RegexpMapConstIter const & rOther) const;
        // for free operator ==(), operator !=()

private:
    RegexpMapIterImpl< Val > * m_pImpl;

    RegexpMapConstIter(RegexpMapIterImpl< Val > * pTheImpl);
};

//============================================================================
template< typename Val >
class RegexpMapIter: public RegexpMapConstIter< Val >
{
    friend class RegexpMap< Val >; // to access ctor

public:
    RegexpMapIter() {}

    RegexpMapIter & operator ++();

    RegexpMapIter operator ++(int);

    RegexpMapEntry< Val > & operator *();

    RegexpMapEntry< Val > const & operator *() const;

    RegexpMapEntry< Val > * operator ->();

    RegexpMapEntry< Val > const * operator ->() const;

private:
    RegexpMapIter(RegexpMapIterImpl< Val > * pTheImpl);
};

//============================================================================
template< typename Val > struct RegexpMapImpl;
    // MSC doesn't like this to be a RegexpMap member class...

template< typename Val >
class RegexpMap
{
public:
    typedef sal_uInt32 size_type;
    typedef RegexpMapIter< Val > iterator;
    typedef RegexpMapConstIter< Val > const_iterator;

    RegexpMap();

    RegexpMap(RegexpMap const & rOther);

    ~RegexpMap();

    RegexpMap & operator =(RegexpMap const & rOther);

    bool add(rtl::OUString const & rKey, Val const & rValue, bool bOverwrite,
             rtl::OUString * pReverse = 0);
        // throws com::sun::star::lang::IllegalArgumentException

    iterator find(rtl::OUString const & rKey, rtl::OUString * pReverse = 0);
        // throws com::sun::star::lang::IllegalArgumentException

    void erase(iterator const & rPos);

    iterator begin();

    const_iterator begin() const;

    iterator end();

    const_iterator end() const;

    bool empty() const;

    size_type size() const;

    Val const * map(rtl::OUString const & rString,
                    rtl::OUString * pTranslation = 0, bool * pTranslated = 0)
        const;

private:
    RegexpMapImpl< Val > * m_pImpl;
};

}

//============================================================================
template< typename Val >
inline bool operator ==(ucb::RegexpMapConstIter< Val > const & rIter1,
                        ucb::RegexpMapConstIter< Val > const & rIter2)
{
    return rIter1.equals(rIter2);
}

template< typename Val >
inline bool operator !=(ucb::RegexpMapConstIter< Val > const & rIter1,
                        ucb::RegexpMapConstIter< Val > const & rIter2)
{
    return !rIter1.equals(rIter2);
}

#endif // _UCB_REGEXPMAP_HXX_

<commit_msg>INTEGRATION: CWS bgdlremove (1.3.108); FILE MERGED 2007/05/18 14:06:49 kso 1.3.108.1: #i77419# - cleanup of ucbhelper namespaces.<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: regexpmap.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: ihi $ $Date: 2007-06-05 17:53:04 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _UCB_REGEXPMAP_HXX_
#define _UCB_REGEXPMAP_HXX_

#ifndef _RTL_OUSTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _SAL_TYPES_H_
#include <sal/types.h>
#endif

namespace ucb_impl {

template< typename Val > class RegexpMap;
template< typename Val > class RegexpMapIter;

//============================================================================
template< typename Val >
class RegexpMapEntry
{
public:
    inline RegexpMapEntry(rtl::OUString const & rTheRegexp,
                          Val * pTheValue):
        m_aRegexp(rTheRegexp), m_pValue(pTheValue) {}

    rtl::OUString getRegexp() const { return m_aRegexp; }

    Val const & getValue() const { return *m_pValue; }

    Val & getValue() { return *m_pValue; }

private:
    rtl::OUString m_aRegexp;
    Val * m_pValue;
};

//============================================================================
template< typename Val > class RegexpMapIterImpl;
    // MSC doesn't like this to be a private RegexpMapConstIter member
    // class...

template< typename Val >
class RegexpMapConstIter
{
    friend class RegexpMap< Val >; // to access m_pImpl, ctor
    friend class RegexpMapIter< Val >; // to access m_pImpl, ctor

public:
    RegexpMapConstIter();

    RegexpMapConstIter(RegexpMapConstIter const & rOther);

    ~RegexpMapConstIter();

    RegexpMapConstIter & operator =(RegexpMapConstIter const & rOther);

    RegexpMapConstIter & operator ++();

    RegexpMapConstIter operator ++(int);

    RegexpMapEntry< Val > const & operator *() const;

    RegexpMapEntry< Val > const * operator ->() const;

    bool equals(RegexpMapConstIter const & rOther) const;
        // for free operator ==(), operator !=()

private:
    RegexpMapIterImpl< Val > * m_pImpl;

    RegexpMapConstIter(RegexpMapIterImpl< Val > * pTheImpl);
};

//============================================================================
template< typename Val >
class RegexpMapIter: public RegexpMapConstIter< Val >
{
    friend class RegexpMap< Val >; // to access ctor

public:
    RegexpMapIter() {}

    RegexpMapIter & operator ++();

    RegexpMapIter operator ++(int);

    RegexpMapEntry< Val > & operator *();

    RegexpMapEntry< Val > const & operator *() const;

    RegexpMapEntry< Val > * operator ->();

    RegexpMapEntry< Val > const * operator ->() const;

private:
    RegexpMapIter(RegexpMapIterImpl< Val > * pTheImpl);
};

//============================================================================
template< typename Val > struct RegexpMapImpl;
    // MSC doesn't like this to be a RegexpMap member class...

template< typename Val >
class RegexpMap
{
public:
    typedef sal_uInt32 size_type;
    typedef RegexpMapIter< Val > iterator;
    typedef RegexpMapConstIter< Val > const_iterator;

    RegexpMap();

    RegexpMap(RegexpMap const & rOther);

    ~RegexpMap();

    RegexpMap & operator =(RegexpMap const & rOther);

    bool add(rtl::OUString const & rKey, Val const & rValue, bool bOverwrite,
             rtl::OUString * pReverse = 0);
        // throws com::sun::star::lang::IllegalArgumentException

    iterator find(rtl::OUString const & rKey, rtl::OUString * pReverse = 0);
        // throws com::sun::star::lang::IllegalArgumentException

    void erase(iterator const & rPos);

    iterator begin();

    const_iterator begin() const;

    iterator end();

    const_iterator end() const;

    bool empty() const;

    size_type size() const;

    Val const * map(rtl::OUString const & rString,
                    rtl::OUString * pTranslation = 0, bool * pTranslated = 0)
        const;

private:
    RegexpMapImpl< Val > * m_pImpl;
};

}

//============================================================================
template< typename Val >
inline bool operator ==(ucb_impl::RegexpMapConstIter< Val > const & rIter1,
                        ucb_impl::RegexpMapConstIter< Val > const & rIter2)
{
    return rIter1.equals(rIter2);
}

template< typename Val >
inline bool operator !=(ucb_impl::RegexpMapConstIter< Val > const & rIter1,
                        ucb_impl::RegexpMapConstIter< Val > const & rIter2)
{
    return !rIter1.equals(rIter2);
}

#endif // _UCB_REGEXPMAP_HXX_

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: htmlitem.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: vg $ $Date: 2007-09-18 14:29:36 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <precomp.h>
#include <udm/html/htmlitem.hxx>

// NOT FULLY DECLARED SERVICES

namespace csi
{
namespace html
{

using namespace csi::xml;

template <class ELEM>
inline ELEM &
PushElem( Element &     i_rMain,
          DYN ELEM *    let_dpSub,
          DYN Item *    let_dpItem )
{
    i_rMain << let_dpSub;
    if ( let_dpItem != 0 )
        *let_dpSub << let_dpItem;
    return *let_dpSub;
}

bool
Body::LineBreakAfterBeginTag() const
{
     return true;
}

#ifndef COMPATIBLE_NETSCAPE_47
bool
HorizontalLine::LineBreakAfterBeginTag() const
{
     return true;
}
#endif

Image::Image( const String &   i_sSrc,
              const String &   i_sWidth,
              const String &   i_sHeight,
              const String &   i_sAlign,
              const String &   i_sBorder )
    :   AnEmptyElement( "img" )
{
    *this << new AnAttribute(String("src"),i_sSrc)
          << new AnAttribute(String("width"),i_sWidth)
          << new AnAttribute(String("height"),i_sHeight)
          << new AnAttribute(String("align"),i_sAlign)
          << new AnAttribute(String("border"),i_sBorder);
}

bool
Paragraph::LineBreakAfterEndTag() const
{
     return true;
}

const char *
Headline::sTags[6] = { "h1", "h2", "h3", "h4", "h5", "h6" };

bool
Headline::LineBreakAfterEndTag() const
{
     return true;
}

#ifndef COMPATIBLE_NETSCAPE_47
bool
LineBreak::LineBreakAfterBeginTag() const
{
     return true;
}
#endif

bool
TableCell::LineBreakAfterEndTag() const
{
     return true;
}

TableCell &
TableRow::AddCell( DYN Item * let_dpItem )
{
    return PushElem( *this, new TableCell, let_dpItem );
}

bool
TableRow::LineBreakAfterBeginTag() const
{
     return true;
}

Table::Table( const String &   i_sBorder,
              const String &   i_sWidth,
              const String &   i_sCellPadding,
              const String &   i_sCellSpacing  )
    :   csi::xml::AnElement("table")
{
    if ( i_sBorder.length() > 0 )
        *this << new AnAttribute(String("border"),i_sBorder);
    if ( i_sBorder.length() > 0 )
        *this << new AnAttribute(String("width"),i_sWidth);
    if ( i_sBorder.length() > 0 )
        *this << new AnAttribute(String("cellpadding"),i_sCellPadding);
    if ( i_sBorder.length() > 0 )
        *this << new AnAttribute(String("cellspacing"),i_sCellSpacing);
}

TableRow &
Table::AddRow()
{
    TableRow * ret = new TableRow;
    *this << ret;
    return *ret;
}

bool
Table::FinishEmptyTag_XmlStyle() const
{
     return false;
}

bool
Table::LineBreakAfterBeginTag() const
{
     return true;
}

bool
DefListTerm::LineBreakAfterEndTag() const
{
     return true;
}

bool
DefListDefinition::LineBreakAfterEndTag() const
{
     return true;
}

DefListTerm &
DefList::AddTerm( DYN csi::xml::Item* let_dpItem )
{
    return PushElem( *this, new DefListTerm, let_dpItem );
}

DefListDefinition &
DefList::AddDefinition( DYN csi::xml::Item* let_dpItem )
{
    return PushElem( *this, new DefListDefinition, let_dpItem );
}

bool
DefList::LineBreakAfterBeginTag() const
{
     return true;
}

bool
DefList::FinishEmptyTag_XmlStyle() const
{
     return false;
}

bool
ListItem::LineBreakAfterEndTag() const
{
     return true;
}

ListItem &
NumeratedList::AddItem( DYN csi::xml::Item* let_dpItem )
{
    return PushElem( *this, new ListItem, let_dpItem );
}

bool
NumeratedList::LineBreakAfterBeginTag() const
{
     return true;
}

ListItem &
SimpleList::AddItem( DYN csi::xml::Item* let_dpItem )
{
    return PushElem( *this, new ListItem, let_dpItem );
}

bool
SimpleList::LineBreakAfterBeginTag() const
{
     return true;
}

}   // namespace html
}   // namespace csi
<commit_msg>INTEGRATION: CWS changefileheader (1.6.10); FILE MERGED 2008/03/31 13:06:21 rt 1.6.10.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: htmlitem.cxx,v $
 * $Revision: 1.7 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <precomp.h>
#include <udm/html/htmlitem.hxx>

// NOT FULLY DECLARED SERVICES

namespace csi
{
namespace html
{

using namespace csi::xml;

template <class ELEM>
inline ELEM &
PushElem( Element &     i_rMain,
          DYN ELEM *    let_dpSub,
          DYN Item *    let_dpItem )
{
    i_rMain << let_dpSub;
    if ( let_dpItem != 0 )
        *let_dpSub << let_dpItem;
    return *let_dpSub;
}

bool
Body::LineBreakAfterBeginTag() const
{
     return true;
}

#ifndef COMPATIBLE_NETSCAPE_47
bool
HorizontalLine::LineBreakAfterBeginTag() const
{
     return true;
}
#endif

Image::Image( const String &   i_sSrc,
              const String &   i_sWidth,
              const String &   i_sHeight,
              const String &   i_sAlign,
              const String &   i_sBorder )
    :   AnEmptyElement( "img" )
{
    *this << new AnAttribute(String("src"),i_sSrc)
          << new AnAttribute(String("width"),i_sWidth)
          << new AnAttribute(String("height"),i_sHeight)
          << new AnAttribute(String("align"),i_sAlign)
          << new AnAttribute(String("border"),i_sBorder);
}

bool
Paragraph::LineBreakAfterEndTag() const
{
     return true;
}

const char *
Headline::sTags[6] = { "h1", "h2", "h3", "h4", "h5", "h6" };

bool
Headline::LineBreakAfterEndTag() const
{
     return true;
}

#ifndef COMPATIBLE_NETSCAPE_47
bool
LineBreak::LineBreakAfterBeginTag() const
{
     return true;
}
#endif

bool
TableCell::LineBreakAfterEndTag() const
{
     return true;
}

TableCell &
TableRow::AddCell( DYN Item * let_dpItem )
{
    return PushElem( *this, new TableCell, let_dpItem );
}

bool
TableRow::LineBreakAfterBeginTag() const
{
     return true;
}

Table::Table( const String &   i_sBorder,
              const String &   i_sWidth,
              const String &   i_sCellPadding,
              const String &   i_sCellSpacing  )
    :   csi::xml::AnElement("table")
{
    if ( i_sBorder.length() > 0 )
        *this << new AnAttribute(String("border"),i_sBorder);
    if ( i_sBorder.length() > 0 )
        *this << new AnAttribute(String("width"),i_sWidth);
    if ( i_sBorder.length() > 0 )
        *this << new AnAttribute(String("cellpadding"),i_sCellPadding);
    if ( i_sBorder.length() > 0 )
        *this << new AnAttribute(String("cellspacing"),i_sCellSpacing);
}

TableRow &
Table::AddRow()
{
    TableRow * ret = new TableRow;
    *this << ret;
    return *ret;
}

bool
Table::FinishEmptyTag_XmlStyle() const
{
     return false;
}

bool
Table::LineBreakAfterBeginTag() const
{
     return true;
}

bool
DefListTerm::LineBreakAfterEndTag() const
{
     return true;
}

bool
DefListDefinition::LineBreakAfterEndTag() const
{
     return true;
}

DefListTerm &
DefList::AddTerm( DYN csi::xml::Item* let_dpItem )
{
    return PushElem( *this, new DefListTerm, let_dpItem );
}

DefListDefinition &
DefList::AddDefinition( DYN csi::xml::Item* let_dpItem )
{
    return PushElem( *this, new DefListDefinition, let_dpItem );
}

bool
DefList::LineBreakAfterBeginTag() const
{
     return true;
}

bool
DefList::FinishEmptyTag_XmlStyle() const
{
     return false;
}

bool
ListItem::LineBreakAfterEndTag() const
{
     return true;
}

ListItem &
NumeratedList::AddItem( DYN csi::xml::Item* let_dpItem )
{
    return PushElem( *this, new ListItem, let_dpItem );
}

bool
NumeratedList::LineBreakAfterBeginTag() const
{
     return true;
}

ListItem &
SimpleList::AddItem( DYN csi::xml::Item* let_dpItem )
{
    return PushElem( *this, new ListItem, let_dpItem );
}

bool
SimpleList::LineBreakAfterBeginTag() const
{
     return true;
}

}   // namespace html
}   // namespace csi
<|endoftext|>


<commit_before>/*
 * Copyright 2011, 2012 Esrille Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// A test harness for the implementation report of
//       the CSS2.1 Conformance Test Suite
// http://test.csswg.org/suites/css2.1/20110323/

#include <unistd.h>
#include <sys/wait.h>

#include <cstring>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

#include <boost/version.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>

enum {
    INTERACTIVE = 0,
    HEADLESS,
    REPORT,
    UPDATE,

    // exit status codes
    ES_PASS = 0,
    ES_FATAL,
    ES_FAIL,
    ES_INVALID,
    ES_NA,
    ES_SKIP,
    ES_UNCERTAIN
};

const char* StatusStrings[] = {
    "pass",
    "fatal",
    "fail",
    "invalid",
    "?",
    "skip",
    "uncertain",
};

struct ForkStatus {
    pid_t       pid;
    std::string url;
    int         code;
public:
    ForkStatus() : pid(-1), code(-1) {}
};

size_t forkMax = 1;
size_t forkTop = 0;
size_t forkCount = 0;
std::vector<ForkStatus> forkStates(1);

size_t uncertainCount = 0;

int processOutput(std::istream& stream, std::string& result)
{
    std::string output;
    bool completed = false;
    while (std::getline(stream, output)) {
        if (!completed) {
            if (output == "## complete")
                completed = true;
            continue;
        }
        if (output == "##")
            break;
        result += output + '\n';
    }
    return 0;
}

int runTest(int argc, char* argv[], std::string userStyle, std::string testFonts, std::string url, std::string& result, unsigned timeout)
{
    int pipefd[2];
    pipe(pipefd);
    char testfontsOption[] = "-testfonts";

    pid_t pid = fork();
    if (pid == -1) {
        std::cerr << "error: no more process to create\n";
        return -1;
    }
    if (pid == 0) {
        close(1);
        dup(pipefd[1]);
        close(pipefd[0]);
        int argi = argc - 1;
        if (!userStyle.empty())
            argv[argi++] = strdup(userStyle.c_str());
        if (testFonts == "on")
            argv[argi++] = testfontsOption;
        url = "http://localhost/suites/css2.1/20110323/" + url;
        // url = "http://test.csswg.org/suites/css2.1/20110323/" + url;
        argv[argi++] = strdup(url.c_str());
        argv[argi] = 0;
        if (timeout)
            alarm(timeout); // Terminate the process if it does not complete in 'timeout' seconds.
        execvp(argv[0], argv);
        exit(EXIT_FAILURE);
    }
    close(pipefd[1]);

#if 104400 <= BOOST_VERSION
    boost::iostreams::stream<boost::iostreams::file_descriptor_source> stream(pipefd[0], boost::iostreams::close_handle);
#else
    boost::iostreams::stream<boost::iostreams::file_descriptor_source> stream(pipefd[0], true);
#endif
    processOutput(stream, result);
    return pid;
}

void killTest(int pid)
{
    int status;
    kill(pid, SIGTERM);
    if (wait(&status) == -1)
        std::cerr << "error: failed to wait for a test process to complete\n";
}

bool loadLog(const std::string& path, std::string& result, std::string& log)
{
    std::ifstream file(path.c_str());
    if (!file) {
        result = "?";
        return false;
    }
    std::string line;
    std::getline(file, line);
    size_t pos = line.find('\t');
    if (pos != std::string::npos)
        result = line.substr(pos + 1);
    else {
        result = "?";
        return false;
    }
    log.clear();
    while (std::getline(file, line))
        log += line + '\n';
    return true;
}

bool saveLog(const std::string& path, const std::string& url, const std::string& result, const std::string& log)
{
    std::ofstream file(path.c_str(), std::ios_base::out | std::ios_base::trunc);
    if (!file) {
        std::cerr << "error: failed to open the report file\n";
        return false;
    }
    file << "# " << url.c_str() << '\t' << result << '\n' << log;
    file.flush();
    file.close();
    return true;
}

std::string test(int mode, int argc, char* argv[], const std::string& url, const std::string& userStyle, const std::string& testFonts, unsigned timeout)
{
    std::string path(url);
    size_t pos = path.rfind('.');
    if (pos != std::string::npos) {
        path.erase(pos);
        path += ".log";
    }
    std::string evaluation;
    std::string log;
    loadLog(path, evaluation, log);

    pid_t pid = -1;
    std::string output;
    switch (mode) {
    case REPORT:
        break;
    case UPDATE:
        if (evaluation[0] == '?')
            break;
        // FALL THROUGH
    default:
        pid = runTest(argc, argv, userStyle, testFonts, url, output, timeout);
        break;
    }

    std::string result;
    if (0 < pid && output.empty())
        result = "fatal";
    else if (mode == INTERACTIVE) {
        std::cout << "## complete\n" << output;
        std::cout << '[' << url << "] ";
        if (evaluation.empty() || evaluation[0] == '?')
            std::cout << "pass? ";
        else {
            std::cout << evaluation << "? ";
            if (evaluation != "pass")
                std::cout << '\a';
        }
        std::getline(std::cin, result);
        if (result.empty()) {
            if (evaluation.empty() || evaluation[0] == '?')
                result = "pass";
            else
                result = evaluation;
        } else if (result == "p" || result == "\x1b")
            result = "pass";
        else if (result == "f")
            result = "fail";
        else if (result == "i")
            result = "invalid";
        else if (result == "k") // keep
            result = evaluation;
        else if (result == "n")
            result = "na";
        else if (result == "s")
            result = "skip";
        else if (result == "u")
            result = "uncertain";
        else if (result == "q" || result == "quit")
            exit(EXIT_FAILURE);
        else if (result == "z")
            result = "undo";
        if (result != "undo" && !saveLog(path, url, result, output)) {
            std::cerr << "error: failed to open the report file\n";
            exit(EXIT_FAILURE);
        }
    } else if (mode == HEADLESS) {
        if (evaluation != "?" && output != log)
            result = "uncertain";
        else
            result = evaluation;
    } else if (mode == REPORT) {
        result = evaluation;
    } else if (mode == UPDATE) {
        result = evaluation;
        if (result[0] != '?') {
            if (!saveLog(path, url, result, output)) {
                std::cerr << "error: failed to open the report file\n";
                exit(EXIT_FAILURE);
            }
        }
    }

    if (0 < pid)
        killTest(pid);
    if (mode != INTERACTIVE && result[0] != '?')
        std::cout << url << '\t' << result << '\n';
    return result;
}

int reduce(std::ostream& report, int option = 0)
{
    size_t count;
    int op = (forkCount < forkMax) ? option : 0;
    for (count = 0; count < forkCount; ++count) {
        int status;
        pid_t pid = waitpid(-1, &status, op);
        if (pid == 0)
            break;
        for (size_t i = 0; i < forkCount; ++i) {
            auto s = &forkStates[(forkTop + i) % forkMax];
            if (s->pid == pid) {
                s->code = WIFEXITED(status) ? WEXITSTATUS(status) : ES_FATAL;
                if (i == 0)
                    op = option;
                break;
            }
        }
    }
    if (0 < count) {
        for (count = 0; count < forkMax; ++count) {
            auto s = &forkStates[(forkTop + count) % forkMax];
            if (s->code == -1)
                break;
            if (s->code == ES_UNCERTAIN)
                ++uncertainCount;
            if (s->code != ES_NA)
                std::cout << s->url << '\t' << StatusStrings[s->code] << '\n';
            report << s->url << '\t' << StatusStrings[s->code] << '\n';
            s->code = -1;
        }
        assert(count <= forkCount);
        forkTop += count;
        forkTop %= forkMax;
        forkCount -= count;
    }
    return count;
}

void map(std::ostream& report, int mode, int argc, char* argv[], const std::string& url, const std::string& userStyle, const std::string& testFonts, unsigned timeout)
{
    assert(forkCount < forkMax);
    pid_t pid = fork();
    if (pid == -1) {
        std::cerr << "error: no more process to create\n";
        exit(EXIT_FAILURE);
    }
    if (pid == 0) {
        std::string path(url);
        size_t pos = path.rfind('.');
        if (pos != std::string::npos) {
            path.erase(pos);
            path += ".log";
        }
        std::string evaluation;
        std::string log;
        loadLog(path, evaluation, log);

        pid_t pid = -1;
        std::string output;
        switch (mode) {
        case UPDATE:
            if (evaluation[0] == '?')
                break;
            // FALL THROUGH
        default:
            pid = runTest(argc, argv, userStyle, testFonts, url, output, timeout);
            break;
        }

        std::string result;
        if (0 < pid && output.empty())
            result = "fatal";
        else if (mode == HEADLESS) {
            if (evaluation != "?" && output != log)
                result = "uncertain";
            else
                result = evaluation;
        } else if (mode == UPDATE) {
            result = evaluation;
            if (result[0] != '?') {
                if (!saveLog(path, url, result, output)) {
                    std::cerr << "error: failed to open the report file\n";
                    exit(EXIT_FAILURE);
                }
            }
        }
        if (0 < pid)
            killTest(pid);
        int status = ES_NA;
        if (!result.compare(0, 4, "pass"))
            status = ES_PASS;
        else if (!result.compare(0, 5, "fatal"))
            status = ES_FATAL;
        else if (!result.compare(0, 4, "fail"))
            status = ES_FAIL;
        else if (!result.compare(0, 7, "invalid"))
            status = ES_INVALID;
        else if (!result.compare(0, 4, "skip"))
            status = ES_SKIP;
        else if (!result.compare(0, 9, "uncertain"))
            status = ES_UNCERTAIN;
        exit(status);
    } else {
        auto s = &forkStates[(forkTop + forkCount) % forkMax];
        s->url = url;
        s->pid = pid;
        ++forkCount;
        reduce(report, WNOHANG);
    }
}

int main(int argc, char* argv[])
{
    int mode = HEADLESS;
    unsigned timeout = 10;

    int argi = 1;
    while (*argv[argi] == '-') {
        switch (argv[argi][1]) {
        case 'i':
            mode = INTERACTIVE;
            timeout = 0;
            break;
        case 'r':
            mode = REPORT;
            break;
        case 'u':
            mode = UPDATE;
            break;
        case 'j':
            forkMax = strtoul(argv[argi] + 2, 0, 10);
            if (forkMax == 0)
                forkMax = 1;
            forkStates.resize(forkMax);
            break;
        default:
            break;
        }
        ++argi;
    }

    if (argc < argi + 2) {
        std::cout << "usage: " << argv[0] << " [-i] report.data command [argument ...]\n";
        return EXIT_FAILURE;
    }

    std::ifstream data(argv[argi]);
    if (!data) {
        std::cerr << "error: " << argv[argi] << ": no such file\n";
        return EXIT_FAILURE;
    }

    std::ofstream report("report.data", std::ios_base::out | std::ios_base::trunc);
    if (!report) {
        std::cerr << "error: failed to open the report file\n";
        return EXIT_FAILURE;
    }

    char* args[argc - argi + 3];
    for (int i = 2; i < argc; ++i)
        args[i - 2] = argv[i + argi - 1];
    args[argc - argi] = args[argc - argi + 1] = args[argc - argi + 2] = 0;

    std::string result;
    std::string url;
    std::string undo;
    std::string userStyle;
    std::string testFonts;
    bool redo = false;
    while (data) {
        if (result == "undo") {
            std::swap(url, undo);
            redo = true;
        } else if (redo) {
            std::swap(url, undo);
            redo = false;
        } else {
            std::string line;
            std::getline(data, line);
            if (line.empty())
                continue;
            if (line == "testname    result  comment") {
                report << line << '\n';
                continue;
            }
            if (line[0] == '#') {
                if (line.compare(1, 9, "userstyle") == 0) {
                    if (10 < line.length()) {
                        std::stringstream s(line.substr(10), std::stringstream::in);
                        s >> userStyle;
                    } else
                        userStyle.clear();
                } else if (line.compare(1, 9, "testfonts") == 0) {
                    if (10 < line.length()) {
                        std::stringstream s(line.substr(10), std::stringstream::in);
                        s >> testFonts;
                    } else
                        testFonts.clear();
                }
                reduce(report);
                report << line << '\n';
                continue;
            }
            undo = url;
            std::stringstream s(line, std::stringstream::in);
            s >> url;
        }
        if (url.empty())
            continue;

        switch (mode) {
        case HEADLESS:
        case UPDATE:
            map(report, mode, argc - argi, args, url, userStyle, testFonts, timeout);
            break;
        default:
            result = test(mode, argc - argi, args, url, userStyle, testFonts, timeout);
            if (result != "undo")
                report << url << '\t' << result << '\n';
            break;
        }
    }
    if (mode == HEADLESS || mode == UPDATE)
        reduce(report);
    report.close();

    return (0 < uncertainCount) ? EXIT_FAILURE : EXIT_SUCCESS;
}
<commit_msg>(harness) : Add -g option to generate log files from the report data.<commit_after>/*
 * Copyright 2011, 2012 Esrille Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// A test harness for the implementation report of
//       the CSS2.1 Conformance Test Suite
// http://test.csswg.org/suites/css2.1/20110323/

#include <unistd.h>
#include <sys/wait.h>

#include <cstring>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

#include <boost/version.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>

enum {
    INTERACTIVE = 0,
    HEADLESS,
    REPORT,
    UPDATE,
    GENERATE,

    // exit status codes
    ES_PASS = 0,
    ES_FATAL,
    ES_FAIL,
    ES_INVALID,
    ES_NA,
    ES_SKIP,
    ES_UNCERTAIN
};

const char* StatusStrings[] = {
    "pass",
    "fatal",
    "fail",
    "invalid",
    "?",
    "skip",
    "uncertain",
};

struct ForkStatus {
    pid_t       pid;
    std::string url;
    int         code;
public:
    ForkStatus() : pid(-1), code(-1) {}
};

size_t forkMax = 1;
size_t forkTop = 0;
size_t forkCount = 0;
std::vector<ForkStatus> forkStates(1);

size_t uncertainCount = 0;

int processOutput(std::istream& stream, std::string& result)
{
    std::string output;
    bool completed = false;
    while (std::getline(stream, output)) {
        if (!completed) {
            if (output == "## complete")
                completed = true;
            continue;
        }
        if (output == "##")
            break;
        result += output + '\n';
    }
    return 0;
}

int runTest(int argc, char* argv[], std::string userStyle, std::string testFonts, std::string url, std::string& result, unsigned timeout)
{
    int pipefd[2];
    pipe(pipefd);
    char testfontsOption[] = "-testfonts";

    pid_t pid = fork();
    if (pid == -1) {
        std::cerr << "error: no more process to create\n";
        return -1;
    }
    if (pid == 0) {
        close(1);
        dup(pipefd[1]);
        close(pipefd[0]);
        int argi = argc - 1;
        if (!userStyle.empty())
            argv[argi++] = strdup(userStyle.c_str());
        if (testFonts == "on")
            argv[argi++] = testfontsOption;
        url = "http://localhost/suites/css2.1/20110323/" + url;
        // url = "http://test.csswg.org/suites/css2.1/20110323/" + url;
        argv[argi++] = strdup(url.c_str());
        argv[argi] = 0;
        if (timeout)
            alarm(timeout); // Terminate the process if it does not complete in 'timeout' seconds.
        execvp(argv[0], argv);
        exit(EXIT_FAILURE);
    }
    close(pipefd[1]);

#if 104400 <= BOOST_VERSION
    boost::iostreams::stream<boost::iostreams::file_descriptor_source> stream(pipefd[0], boost::iostreams::close_handle);
#else
    boost::iostreams::stream<boost::iostreams::file_descriptor_source> stream(pipefd[0], true);
#endif
    processOutput(stream, result);
    return pid;
}

void killTest(int pid)
{
    int status;
    kill(pid, SIGTERM);
    if (wait(&status) == -1)
        std::cerr << "error: failed to wait for a test process to complete\n";
}

bool loadLog(const std::string& path, std::string& result, std::string& log)
{
    std::ifstream file(path.c_str());
    if (!file) {
        result = "?";
        return false;
    }
    std::string line;
    std::getline(file, line);
    size_t pos = line.find('\t');
    if (pos != std::string::npos)
        result = line.substr(pos + 1);
    else {
        result = "?";
        return false;
    }
    log.clear();
    while (std::getline(file, line))
        log += line + '\n';
    return true;
}

bool saveLog(const std::string& path, const std::string& url, const std::string& result, const std::string& log)
{
    std::ofstream file(path.c_str(), std::ios_base::out | std::ios_base::trunc);
    if (!file) {
        std::cerr << "error: failed to open the report file\n";
        return false;
    }
    file << "# " << url.c_str() << '\t' << result << '\n' << log;
    file.flush();
    file.close();
    return true;
}

std::string test(int mode, int argc, char* argv[], const std::string& url, const std::string& userStyle, const std::string& testFonts, unsigned timeout)
{
    std::string path(url);
    size_t pos = path.rfind('.');
    if (pos != std::string::npos) {
        path.erase(pos);
        path += ".log";
    }
    std::string evaluation;
    std::string log;
    loadLog(path, evaluation, log);

    pid_t pid = -1;
    std::string output;
    switch (mode) {
    case REPORT:
        break;
    case UPDATE:
        if (evaluation[0] == '?')
            break;
        // FALL THROUGH
    default:
        pid = runTest(argc, argv, userStyle, testFonts, url, output, timeout);
        break;
    }

    std::string result;
    if (0 < pid && output.empty())
        result = "fatal";
    else if (mode == INTERACTIVE) {
        std::cout << "## complete\n" << output;
        std::cout << '[' << url << "] ";
        if (evaluation.empty() || evaluation[0] == '?')
            std::cout << "pass? ";
        else {
            std::cout << evaluation << "? ";
            if (evaluation != "pass")
                std::cout << '\a';
        }
        std::getline(std::cin, result);
        if (result.empty()) {
            if (evaluation.empty() || evaluation[0] == '?')
                result = "pass";
            else
                result = evaluation;
        } else if (result == "p" || result == "\x1b")
            result = "pass";
        else if (result == "f")
            result = "fail";
        else if (result == "i")
            result = "invalid";
        else if (result == "k") // keep
            result = evaluation;
        else if (result == "n")
            result = "na";
        else if (result == "s")
            result = "skip";
        else if (result == "u")
            result = "uncertain";
        else if (result == "q" || result == "quit")
            exit(EXIT_FAILURE);
        else if (result == "z")
            result = "undo";
        if (result != "undo" && !saveLog(path, url, result, output)) {
            std::cerr << "error: failed to open the report file\n";
            exit(EXIT_FAILURE);
        }
    } else if (mode == HEADLESS) {
        if (evaluation != "?" && output != log)
            result = "uncertain";
        else
            result = evaluation;
    } else if (mode == REPORT) {
        result = evaluation;
    } else if (mode == UPDATE) {
        result = evaluation;
        if (result[0] != '?') {
            if (!saveLog(path, url, result, output)) {
                std::cerr << "error: failed to open the report file\n";
                exit(EXIT_FAILURE);
            }
        }
    }

    if (0 < pid)
        killTest(pid);
    if (mode != INTERACTIVE && result[0] != '?')
        std::cout << url << '\t' << result << '\n';
    return result;
}

int reduce(std::ostream& report, int option = 0)
{
    size_t count;
    int op = (forkCount < forkMax) ? option : 0;
    for (count = 0; count < forkCount; ++count) {
        int status;
        pid_t pid = waitpid(-1, &status, op);
        if (pid == 0)
            break;
        for (size_t i = 0; i < forkCount; ++i) {
            auto s = &forkStates[(forkTop + i) % forkMax];
            if (s->pid == pid) {
                s->code = WIFEXITED(status) ? WEXITSTATUS(status) : ES_FATAL;
                if (i == 0)
                    op = option;
                break;
            }
        }
    }
    if (0 < count) {
        for (count = 0; count < forkMax; ++count) {
            auto s = &forkStates[(forkTop + count) % forkMax];
            if (s->code == -1)
                break;
            if (s->code == ES_UNCERTAIN)
                ++uncertainCount;
            if (s->code != ES_NA)
                std::cout << s->url << '\t' << StatusStrings[s->code] << '\n';
            report << s->url << '\t' << StatusStrings[s->code] << '\n';
            s->code = -1;
        }
        assert(count <= forkCount);
        forkTop += count;
        forkTop %= forkMax;
        forkCount -= count;
    }
    return count;
}

void map(std::ostream& report, int mode, int argc, char* argv[], const std::string& url, const std::string& userStyle, const std::string& testFonts, unsigned timeout, std::string result = "")
{
    assert(forkCount < forkMax);
    pid_t pid = fork();
    if (pid == -1) {
        std::cerr << "error: no more process to create\n";
        exit(EXIT_FAILURE);
    }
    if (pid == 0) {
        std::string path(url);
        size_t pos = path.rfind('.');
        if (pos != std::string::npos) {
            path.erase(pos);
            path += ".log";
        }
        std::string evaluation;
        std::string log;
        loadLog(path, evaluation, log);

        pid_t pid = -1;
        std::string output;
        switch (mode) {
        case GENERATE:
            evaluation = result;
            // FALL THROUGH
        case UPDATE:
            if (evaluation[0] == '?')
                break;
            // FALL THROUGH
        default:
            pid = runTest(argc, argv, userStyle, testFonts, url, output, timeout);
            break;
        }

        if (0 < pid && output.empty())
            result = "fatal";
        else {
            switch (mode) {
            case HEADLESS:
                if (evaluation != "?" && output != log)
                    result = "uncertain";
                else
                    result = evaluation;
                break;
            case UPDATE:
            case GENERATE:
                result = evaluation;
                if (result[0] != '?') {
                    if (!saveLog(path, url, result, output)) {
                        std::cerr << "error: failed to open the report file\n";
                        exit(EXIT_FAILURE);
                    }
                }
                break;
            default:
                break;
            }
        }
        if (0 < pid)
            killTest(pid);
        int status = ES_NA;
        if (!result.compare(0, 4, "pass"))
            status = ES_PASS;
        else if (!result.compare(0, 5, "fatal"))
            status = ES_FATAL;
        else if (!result.compare(0, 4, "fail"))
            status = ES_FAIL;
        else if (!result.compare(0, 7, "invalid"))
            status = ES_INVALID;
        else if (!result.compare(0, 4, "skip"))
            status = ES_SKIP;
        else if (!result.compare(0, 9, "uncertain"))
            status = ES_UNCERTAIN;
        exit(status);
    } else {
        auto s = &forkStates[(forkTop + forkCount) % forkMax];
        s->url = url;
        s->pid = pid;
        ++forkCount;
        reduce(report, WNOHANG);
    }
}

int main(int argc, char* argv[])
{
    int mode = HEADLESS;
    unsigned timeout = 10;

    int argi = 1;
    while (*argv[argi] == '-') {
        switch (argv[argi][1]) {
        case 'i':
            mode = INTERACTIVE;
            timeout = 0;
            break;
        case 'g':
            mode = GENERATE;
            break;
        case 'r':
            mode = REPORT;
            break;
        case 'u':
            mode = UPDATE;
            break;
        case 'j':
            forkMax = strtoul(argv[argi] + 2, 0, 10);
            if (forkMax == 0)
                forkMax = 1;
            forkStates.resize(forkMax);
            break;
        default:
            break;
        }
        ++argi;
    }

    if (argc < argi + 2) {
        std::cout << "usage: " << argv[0] << " [-i] report.data command [argument ...]\n";
        return EXIT_FAILURE;
    }

    std::ifstream data(argv[argi]);
    if (!data) {
        std::cerr << "error: " << argv[argi] << ": no such file\n";
        return EXIT_FAILURE;
    }

    std::ofstream report("report.data", std::ios_base::out | std::ios_base::trunc);
    if (!report) {
        std::cerr << "error: failed to open the report file\n";
        return EXIT_FAILURE;
    }

    char* args[argc - argi + 3];
    for (int i = 2; i < argc; ++i)
        args[i - 2] = argv[i + argi - 1];
    args[argc - argi] = args[argc - argi + 1] = args[argc - argi + 2] = 0;

    std::string result;
    std::string url;
    std::string undo;
    std::string userStyle;
    std::string testFonts;
    bool redo = false;
    while (data) {
        if (result == "undo") {
            std::swap(url, undo);
            redo = true;
        } else if (redo) {
            std::swap(url, undo);
            redo = false;
        } else {
            std::string line;
            std::getline(data, line);
            if (line.empty())
                continue;
            if (line == "testname    result  comment") {
                report << line << '\n';
                continue;
            }
            if (line[0] == '#') {
                if (line.compare(1, 9, "userstyle") == 0) {
                    if (10 < line.length()) {
                        std::stringstream s(line.substr(10), std::stringstream::in);
                        s >> userStyle;
                    } else
                        userStyle.clear();
                } else if (line.compare(1, 9, "testfonts") == 0) {
                    if (10 < line.length()) {
                        std::stringstream s(line.substr(10), std::stringstream::in);
                        s >> testFonts;
                    } else
                        testFonts.clear();
                }
                reduce(report);
                report << line << '\n';
                continue;
            }
            undo = url;
            std::stringstream s(line, std::stringstream::in);
            s >> url;
            s >> result;
            std::getline(s, line);
            result += line;
        }
        if (url.empty())
            continue;
        if (result.empty())
            result = "?";

        switch (mode) {
        case HEADLESS:
        case UPDATE:
        case GENERATE:
            map(report, mode, argc - argi, args, url, userStyle, testFonts, timeout, result);
            break;
        default:
            result = test(mode, argc - argi, args, url, userStyle, testFonts, timeout);
            if (result != "undo")
                report << url << '\t' << result << '\n';
            break;
        }
    }
    if (mode == HEADLESS || mode == UPDATE)
        reduce(report);
    report.close();

    return (0 < uncertainCount) ? EXIT_FAILURE : EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include <nanogui/common.h>
#include <nanogui/opengl.h>
#include <nanogui/theme.h>
#include <nanovg.h>
#include <spdlog/spdlog.h>
#include "graph.hpp"
#include "graphnodelink.hpp"
#include "graphnode.hpp"
#include "connector.hpp"
#include "sink.hpp"

NAMESPACE_BEGIN(QCE);

Connector::Connector(GraphNode *parent, Graph *parentGraph, const std::string &label) :
    nanogui::Widget(parent),
    mParent(parent),
    mParentGraph(parentGraph),
    mLink(nullptr),
    mLabel(label),
    mTextColor(nanogui::Color(0, 0)),
    mIsDragged(false),
    mRelativePosition(Eigen::Vector2i::Zero()),
    mIndex(0)
{
    mFixedSize = mSize = Eigen::Vector2i(15, 15);
}

bool Connector::isConnected()
{
    return mLink != nullptr && mLink->hasTarget();
}

void Connector::draw(NVGcontext* ctx)
{
    refreshRelativePlacement();

    Eigen::Vector2f center = mPos.cast<float>() + mSize.cast<float>() * 0.5f;
    float radius = (int)(mSize.y() * 0.5f);

    // Draw label
    nvgFontSize(ctx, fontSize());
    nvgFontFace(ctx, "sans");
    nvgFillColor(ctx, mEnabled ? mTheme->mTextColor : mTheme->mDisabledTextColor);
    nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
    nvgText(ctx, center.x() + radius + 1.2f, center.y(), mLabel.c_str(), nullptr);

    // Draw body
    nvgBeginPath(ctx);
    nvgCircle(ctx, center.x(), center.y(), radius);
    nvgFillColor(ctx, mTheme->mBorderMedium);
    nvgStrokeColor(ctx, mTheme->mBorderLight);
    nvgStroke(ctx);
    nvgFill(ctx);

    if (hasLink()) {
        NVGpaint knobReverse = nvgLinearGradient(ctx, mPos.x(), center.y() - radius, mPos.x(), center.y() + radius, mTheme->mBorderMedium, mTheme->mBorderLight);

        nvgBeginPath(ctx);
        nvgCircle(ctx, center.x(), center.y(), radius * 0.5f);
        nvgFillColor(ctx, nanogui::Color(150, mEnabled ? 255 : 100));
        nvgStrokePaint(ctx, knobReverse);
        nvgStroke(ctx);
        nvgFill(ctx);
    }
    Widget::draw(ctx);
}

Eigen::Vector2i Connector::preferredSize(NVGcontext *ctx) const
{
    if (mFixedSize != Eigen::Vector2i::Zero()) {
        return mFixedSize;
    }
    nvgFontSize(ctx, fontSize());
    nvgFontFace(ctx, "sans");
    return Eigen::Vector2i(
        nvgTextBounds(ctx, 0, 0, mLabel.c_str(), nullptr, nullptr) +
        1.7f * fontSize(),
        fontSize() * 1.3f
    );
}

void Connector::refreshRelativePlacement()
{
    mRelativePosition = relativePosition();
    mPos = mRelativePosition;
    /*
    spdlog::get("qde")->debug(
        "Connector '{}': At ({},{})",
        mLabel, mPos.x(), mPos.y()
    );
    */
}

bool Connector::mouseButtonEvent(const Eigen::Vector2i &p, int button, bool down, int modifiers)
{
    spdlog::get("qde")->debug(
        "Connector '{}': Received mouse button event at ({},{}): {}, {}",
        mLabel, p.x(), p.y(), button, down
    );
    return Widget::mouseButtonEvent(p, button, down, modifiers);

    /*
    // Check if the event gets handled in parent widget
    if (Widget::mouseButtonEvent(p, button, down, modifiers)) {
        return true;
    }

    if (button == GLFW_MOUSE_BUTTON_1) {
        // We want to handle only the events for state changes.
        // A state change is as follows:
        //
        //  * Active:   Not dragged (0) --> dragged (1)
        //  * Inactive: Dragged (1)     --> not dragged (0)
        //
        //     1----1----1
        //    /           \
        //   /             \
        //  0               0
        //
        // The other possible states do not interest us.

        bool wasDraggedBefore = mIsDragged;
        mIsDragged = down && (p.y() - mPos.y()) < mTheme->mWindowHeaderHeight;
        bool currentState = false;

        if (!wasDraggedBefore && mIsDragged) {
            currentState = true;
        }
        else if (wasDraggedBefore && !mIsDragged) {
            currentState = false;
        }
        mParentGraph->connectorDraggedEvent(this, p, currentState);

        return false;
    }

    return false;
    */
}

bool Connector::mouseDragEvent(const Eigen::Vector2i &p, const Eigen::Vector2i &rel, int button, int modifiers)
{
    spdlog::get("qde")->debug(
        "Connector '{}': Getting dragged: ({},{}), ({},{}), {}, {}",
        mLabel, p.x(), p.y(), rel.x(), rel.y(), button, modifiers
    );
    return Widget::mouseDragEvent(p, rel, button, modifiers);

    /*
    if (Widget::mouseDragEvent(p, rel, button, modifiers)) {
        return true;
    }

    if (mLink) {
        mLink->setTargetPosition(p);
        return true;
    }

    return false;
    */
}

bool Connector::mouseEnterEvent(const Eigen::Vector2i &p, bool enter)
{
    spdlog::get("qde")->debug(
        "Connector '{}': Mouse entered at ({},{})",
        mLabel, p.x(), p.y()
    );
    return Widget::mouseEnterEvent(p, enter);
}

NAMESPACE_END(QCE)
<commit_msg>Fix event handling<commit_after>#include <nanogui/common.h>
#include <nanogui/opengl.h>
#include <nanogui/theme.h>
#include <nanovg.h>
#include <spdlog/spdlog.h>
#include "graph.hpp"
#include "graphnodelink.hpp"
#include "graphnode.hpp"
#include "connector.hpp"
#include "sink.hpp"

NAMESPACE_BEGIN(QCE);

Connector::Connector(GraphNode *parent, Graph *parentGraph, const std::string &label) :
    nanogui::Widget(parent),
    mParent(parent),
    mParentGraph(parentGraph),
    mLink(nullptr),
    mLabel(label),
    mTextColor(nanogui::Color(0, 0)),
    mIsDragged(false),
    mRelativePosition(Eigen::Vector2i::Zero()),
    mIndex(0)
{
    mFixedSize = mSize = Eigen::Vector2i(15, 15);
}

bool Connector::isConnected()
{
    return mLink != nullptr && mLink->hasTarget();
}

void Connector::draw(NVGcontext* ctx)
{
    refreshRelativePlacement();

    Eigen::Vector2f center = mPos.cast<float>() + mSize.cast<float>() * 0.5f;
    float radius = (int)(mSize.y() * 0.5f);

    // Draw label
    nvgFontSize(ctx, fontSize());
    nvgFontFace(ctx, "sans");
    nvgFillColor(ctx, mEnabled ? mTheme->mTextColor : mTheme->mDisabledTextColor);
    nvgTextAlign(ctx, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
    nvgText(ctx, center.x() + radius + 1.2f, center.y(), mLabel.c_str(), nullptr);

    // Draw body
    nvgBeginPath(ctx);
    nvgCircle(ctx, center.x(), center.y(), radius);
    nvgFillColor(ctx, mTheme->mBorderMedium);
    nvgStrokeColor(ctx, mTheme->mBorderLight);
    nvgStroke(ctx);
    nvgFill(ctx);

    if (hasLink()) {
        NVGpaint knobReverse = nvgLinearGradient(ctx, mPos.x(), center.y() - radius, mPos.x(), center.y() + radius, mTheme->mBorderMedium, mTheme->mBorderLight);

        nvgBeginPath(ctx);
        nvgCircle(ctx, center.x(), center.y(), radius * 0.5f);
        nvgFillColor(ctx, nanogui::Color(150, mEnabled ? 255 : 100));
        nvgStrokePaint(ctx, knobReverse);
        nvgStroke(ctx);
        nvgFill(ctx);
    }
    Widget::draw(ctx);
}

Eigen::Vector2i Connector::preferredSize(NVGcontext *ctx) const
{
    if (mFixedSize != Eigen::Vector2i::Zero()) {
        return mFixedSize;
    }
    nvgFontSize(ctx, fontSize());
    nvgFontFace(ctx, "sans");
    return Eigen::Vector2i(
        nvgTextBounds(ctx, 0, 0, mLabel.c_str(), nullptr, nullptr) +
        1.7f * fontSize(),
        fontSize() * 1.3f
    );
}

void Connector::refreshRelativePlacement()
{
    mRelativePosition = relativePosition();
    mPos = mRelativePosition;
    /*
    spdlog::get("qde")->debug(
        "Connector '{}': At ({},{})",
        mLabel, mPos.x(), mPos.y()
    );
    */
}

bool Connector::mouseButtonEvent(const Eigen::Vector2i &p, int button, bool down, int modifiers)
{
    spdlog::get("qde")->debug(
        "Connector '{}': Received mouse button event at ({},{}): {}, {}",
        mLabel, p.x(), p.y(), button, down
    );
    Widget::mouseButtonEvent(p, button, down, modifiers);

    if (button == GLFW_MOUSE_BUTTON_1) {
        // We want to handle only the events for state changes.
        // A state change is as follows:
        //
        //  * Active:   Not dragged (0) --> dragged (1)
        //  * Inactive: Dragged (1)     --> not dragged (0)
        //
        //     1----1----1
        //    /           \
        //   /             \
        //  0               0
        //
        // The other possible states do not interest us.

        bool wasDraggedBefore = mIsDragged;
        mIsDragged = down && (p.y() - mPos.y()) < mTheme->mWindowHeaderHeight;
        bool currentState = false;

        if (!wasDraggedBefore && mIsDragged) {
            currentState = true;
        }
        else if (wasDraggedBefore && !mIsDragged) {
            currentState = false;
        }
        mParentGraph->connectorDraggedEvent(this, p, currentState);

        return true;
    }

    return false;
}

bool Connector::mouseDragEvent(const Eigen::Vector2i &p, const Eigen::Vector2i &rel, int button, int modifiers)
{
    spdlog::get("qde")->debug(
        "Connector '{}': Getting dragged: ({},{}), ({},{}), {}, {}",
        mLabel, p.x(), p.y(), rel.x(), rel.y(), button, modifiers
    );
    Widget::mouseDragEvent(p, rel, button, modifiers);

    if (mLink) {
        mLink->setTargetPosition(p);

        return true;
    }

    return false;
}

bool Connector::mouseEnterEvent(const Eigen::Vector2i &p, bool enter)
{
    spdlog::get("qde")->debug(
        "Connector '{}': Mouse entered at ({},{})",
        mLabel, p.x(), p.y()
    );
    return Widget::mouseEnterEvent(p, enter);
}

NAMESPACE_END(QCE)
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2012 - 2013 Jolla Ltd.
** Contact: http://jolla.com/
**
** This file is part of Qt Creator.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Digia.
**
****************************************************************************/

#include "mertarget.h"
#include "mersdk.h"
#include "merconstants.h"
#include "mersdkmanager.h"
#include "mertoolchain.h"
#include "merqtversion.h"
#include "mersdkkitinformation.h"
#include "mertargetkitinformation.h"
#include <debugger/debuggerkitinformation.h>
#include <qtsupport/qtversionmanager.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionfactory.h>
#include <projectexplorer/toolchainmanager.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <QDir>
#include <QStringList>

namespace Mer {
namespace Internal {

using namespace Mer::Constants;

const char* wrapperScripts[] =
{
    MER_WRAPPER_QMAKE,
    MER_WRAPPER_MAKE,
    MER_WRAPPER_GCC,
    MER_WRAPPER_DEPLOY,
    MER_WRAPPER_RPM,
    MER_WRAPPER_RPMBUILD
};

MerTarget::MerTarget(MerSdk* mersdk):
    m_sdk(mersdk),
    m_defaultGdb(QLatin1String(Constants::MER_DEBUGGER_FILENAME))  //TODO:
{
}

MerTarget::~MerTarget()
{
}

QString MerTarget::name() const
{
    return m_name;
}

void MerTarget::setName(const QString &name)
{
    m_name = name;
}

void MerTarget::setQmakeQuery(const QString &qmakeQuery)
{
    m_qmakeQuery = qmakeQuery;
}

void MerTarget::setGccDumpMachine(const QString &gccMachineDump)
{
    m_gccMachineDump = gccMachineDump;
}

void MerTarget::setDefaultGdb(const QString &name)
{
    m_defaultGdb=name;
}

bool MerTarget::fromMap(const QVariantMap &data)
{
    m_name = data.value(QLatin1String(Constants::MER_TARGET_NAME)).toString();
    m_qmakeQuery = data.value(QLatin1String(Constants::MER_TARGET_QMAKE_DUMP)).toString();
    m_gccMachineDump = data.value(QLatin1String(Constants::MER_TARGET_GCC_DUMP)).toString();
    m_defaultGdb = data.value(QLatin1String(Constants::MER_TARGET_DEFAULT_DEBUGGER)).toString();
    return isValid();
}

QVariantMap MerTarget::toMap() const
{
    QVariantMap result;
    result.insert(QLatin1String(Constants::MER_TARGET_NAME), m_name);
    result.insert(QLatin1String(Constants::MER_TARGET_QMAKE_DUMP), m_qmakeQuery);
    result.insert(QLatin1String(Constants::MER_TARGET_GCC_DUMP), m_gccMachineDump);
    result.insert(QLatin1String(Constants::MER_TARGET_DEFAULT_DEBUGGER), m_defaultGdb);
    return result;
}

bool MerTarget::isValid() const
{
    return !m_name.isEmpty() && !m_qmakeQuery.isEmpty() && !m_gccMachineDump.isEmpty();
}

QString MerTarget::targetPath() const
{
    return MerSdkManager::sdkToolsDirectory() + m_sdk->virtualMachineName() + QLatin1Char('/') + m_name;
}

bool MerTarget::createScripts() const
{
    if (!isValid())
        return false;

    const QString targetPath(this->targetPath());

    QDir targetDir(targetPath);
    if (!targetDir.exists() && !targetDir.mkpath(targetPath)) {
        qWarning() << "Could not create target directory." << targetDir;
        return false;
    }
    bool result = true;
    for (size_t i = 0; i < sizeof(wrapperScripts) / sizeof(wrapperScripts[0]); ++i)
         result &= createScript(targetPath, i);

    const QString qmakepath = targetPath + QLatin1Char('/') + QLatin1String(Constants::QMAKE_QUERY);
    const QString gccpath = targetPath + QLatin1Char('/') + QLatin1String(Constants::GCC_DUMPMACHINE);

    QString patchedQmakeQuery = m_qmakeQuery;
    patchedQmakeQuery.replace(QLatin1String(":/"),
                              QString::fromLatin1(":%1/%2/").arg(m_sdk->sharedTargetsPath()).arg(m_name));

    result &= createCacheFile(qmakepath, patchedQmakeQuery);
    result &= createCacheFile(gccpath, m_gccMachineDump);

    return result;
}

void MerTarget::deleteScripts() const
{
    Utils::FileUtils::removeRecursively(Utils::FileName::fromString(targetPath()));
}

ProjectExplorer::Kit* MerTarget::createKit() const
{
    if (!isValid())
        return 0;
    const QString sysroot(m_sdk->sharedTargetsPath() + QLatin1String("/") + m_name);

    Utils::FileName path = Utils::FileName::fromString(sysroot);
    if (!path.toFileInfo().exists()) {
        qWarning() << "Sysroot does not exist" << sysroot;
        return 0;
    }

    ProjectExplorer::Kit *k = new ProjectExplorer::Kit();
    k->setAutoDetected(true);
    k->setDisplayName(QString::fromLatin1("%1-%2").arg(m_sdk->virtualMachineName(), m_name));
    k->setIconPath(QLatin1String(Constants::MER_OPTIONS_CATEGORY_ICON));
    ProjectExplorer::SysRootKitInformation::setSysRoot(k, Utils::FileName::fromUserInput(sysroot));

    if (m_gccMachineDump.contains(QLatin1String("i486"))) {
        ProjectExplorer::DeviceTypeKitInformation::setDeviceTypeId(k, Constants::MER_DEVICE_TYPE_I486);
    } else if (m_gccMachineDump.contains(QLatin1String("arm"))) {
        ProjectExplorer::DeviceTypeKitInformation::setDeviceTypeId(k, Constants::MER_DEVICE_TYPE_ARM);
    }

    const QString gdb = Utils::HostOsInfo::withExecutableSuffix(m_defaultGdb);
    QString gdbDir = QCoreApplication::applicationDirPath();
    if (Utils::HostOsInfo::isMacHost()) {
        QDir dir = QDir(gdbDir);
        dir.cdUp();
        dir.cdUp();
        dir.cdUp();
        gdbDir = dir.path();
    }
    Utils::FileName gdbFileName = Utils::FileName::fromString(gdbDir + QLatin1String("/") + gdb);

    Debugger::DebuggerKitInformation::setEngineType(k,Debugger::GdbEngineType);
    Debugger::DebuggerKitInformation::setDebuggerCommand(k,gdbFileName);
    MerSdkKitInformation::setSdk(k,m_sdk);
    MerTargetKitInformation::setTargetName(k,name());
    return k;
}

MerQtVersion* MerTarget::createQtVersion() const
{
    const Utils::FileName qmake =
            Utils::FileName::fromString(targetPath() + QLatin1Char('/') +
                                        QLatin1String(Constants::MER_WRAPPER_QMAKE));
    QtSupport::QtVersionManager *qtvm = QtSupport::QtVersionManager::instance();
    // Is there a qtversion present for this qmake?
    QtSupport::BaseQtVersion *qtv = qtvm->qtVersionForQMakeBinary(qmake);
    if (qtv && !qtv->isValid()) {
        qtvm->removeVersion(qtv);
        qtv = 0;
    }
    if (!qtv)
        qtv = new MerQtVersion(qmake, true, targetPath());

    //QtSupport::QtVersionFactory::createQtVersionFromQMakePath(qmake, true, targetPath());

    QTC_ASSERT(qtv && qtv->type() == QLatin1String(Constants::MER_QT), return 0);

    MerQtVersion *merqtv = static_cast<MerQtVersion *>(qtv);
    const QString vmName = m_sdk->virtualMachineName();
    merqtv->setVirtualMachineName(vmName);
    merqtv->setTargetName(m_name);
    merqtv->setDisplayName(
                QString::fromLatin1("Qt %1 in %2 %3").arg(qtv->qtVersionString(),
                                                          vmName, m_name));
    return merqtv;
}

MerToolChain* MerTarget::createToolChain() const
{
    const Utils::FileName gcc =
            Utils::FileName::fromString(targetPath() + QLatin1Char('/') +
                                        QLatin1String(Constants::MER_WRAPPER_GCC));
    ProjectExplorer::ToolChainManager *tcm = ProjectExplorer::ToolChainManager::instance();
    QList<ProjectExplorer::ToolChain *> toolChains = tcm->toolChains();

    foreach (ProjectExplorer::ToolChain *tc, toolChains) {
        if (tc->compilerCommand() == gcc && tc->isAutoDetected()) {
            QTC_ASSERT(tc->type() == QLatin1String(Constants::MER_TOOLCHAIN_TYPE), return 0);
        }
    }

    MerToolChain* mertoolchain = new MerToolChain(true);
    const QString vmName = m_sdk->virtualMachineName();
    mertoolchain->setDisplayName(QString::fromLatin1("GCC (%1 %2)").arg(vmName, m_name));
    mertoolchain->setVirtualMachine(vmName);
    mertoolchain->setTargetName(m_name);
    mertoolchain->setCompilerCommand(gcc);
    return mertoolchain;
}

bool MerTarget::createScript(const QString &targetPath, int scriptIndex) const
{
    bool ok = false;
    const char* wrapperScriptCopy = wrapperScripts[scriptIndex];
    const QFile::Permissions rwrw = QFile::ReadOwner|QFile::ReadUser|QFile::ReadGroup
            |QFile::WriteOwner|QFile::WriteUser|QFile::WriteGroup;
    const QFile::Permissions rwxrwx = rwrw|QFile::ExeOwner|QFile::ExeUser|QFile::ExeGroup;

    QString wrapperScriptCommand = QLatin1String(wrapperScriptCopy);
    if (Utils::HostOsInfo::isWindowsHost())
        wrapperScriptCommand.chop(4); // remove the ".cmd"

    QString wrapperBinaryPath = QCoreApplication::applicationDirPath();
    if (Utils::HostOsInfo::isWindowsHost())
        wrapperBinaryPath += QLatin1String("/merssh.exe");
    else if (Utils::HostOsInfo::isMacHost())
        wrapperBinaryPath += QLatin1String("/../Resources/merssh");
    else
        wrapperBinaryPath += QLatin1String("/merssh");

    using namespace Utils;
    const QString scriptCopyPath = targetPath + QLatin1Char('/') + QLatin1String(wrapperScriptCopy);
    QDir targetDir(targetPath);
    const QString targetName = targetDir.dirName();
    targetDir.cdUp();
    const QString merDevToolsDir = targetDir.canonicalPath();
    QFile script(scriptCopyPath);
    ok = script.open(QIODevice::WriteOnly);
    if (!ok) {
        qWarning() << "Could not open script" << scriptCopyPath;
        return false;
    }

    QString scriptContent;

    if (HostOsInfo::isWindowsHost()) {
        scriptContent += QLatin1String("@echo off\nSetLocal EnableDelayedExpansion\n");
        scriptContent += QLatin1String("set ARGUMENTS=\nFOR %%a IN (%*) DO set ARGUMENTS=!ARGUMENTS! ^ '%%a'\n");
        scriptContent += QLatin1String("set ") +
                QLatin1String(Mer::Constants::MER_SSH_TARGET_NAME) +
                QLatin1Char('=') + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("set ") +
                QLatin1String(Mer::Constants::MER_SSH_SDK_TOOLS) +
                QLatin1Char('=') + merDevToolsDir + QDir::separator() + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("SetLocal DisableDelayedExpansion\n");
        scriptContent += QLatin1Char('"') +
                QDir::toNativeSeparators(wrapperBinaryPath) + QLatin1String("\" ") +
                wrapperScriptCommand + QLatin1Char(' ') + QLatin1String("%ARGUMENTS%\n");
    }

    if (HostOsInfo::isAnyUnixHost()) {
        scriptContent += QLatin1String("#!/bin/bash\n");
        scriptContent += QLatin1String("ARGUMENTS=\"\";for ARGUMENT in \"$@\"; do ARGUMENTS=\"${ARGUMENTS} '${ARGUMENT}'\" ; done;\n");
        scriptContent += QLatin1String("export  ") +
                QLatin1String(Mer::Constants::MER_SSH_TARGET_NAME) +
                QLatin1Char('=') + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("export  ") +
                QLatin1String(Mer::Constants::MER_SSH_SDK_TOOLS) +
                QLatin1Char('=') + merDevToolsDir + QDir::separator() + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("exec ");
        scriptContent += QLatin1Char('"') +
                QDir::toNativeSeparators(wrapperBinaryPath) + QLatin1String("\" ") +
                wrapperScriptCommand + QLatin1Char(' ') + QLatin1String("${ARGUMENTS}");
    }

    ok = script.write(scriptContent.toUtf8()) != -1;
    if (!ok) {
        qWarning() << "Could not write script" << scriptCopyPath;
        return false;
    }

    ok = QFile::setPermissions(scriptCopyPath, rwxrwx);
    if (!ok) {
        qWarning() << "Could not set file permissions on script" << scriptCopyPath;
        return false;
    }
    return ok;
}

bool MerTarget::createCacheFile(const QString &fileName, const QString &content) const
{
    bool ok = false;

    QFile file(fileName);
    ok = file.open(QIODevice::WriteOnly);
    if (!ok) {
        qWarning() << "Could not open cache file" << fileName;
        return false;
    }

    ok = file.write(content.toUtf8()) != -1;
    if (!ok) {
        qWarning() << "Could not write cache file " << fileName;
        return false;
    }
    file.close();
    return ok;
}

bool MerTarget::operator==(const MerTarget &other) const
{
    return m_name == other.m_name && m_qmakeQuery == other.m_qmakeQuery && m_gccMachineDump == other.m_gccMachineDump;
}

}
}
<commit_msg>Jolla: use /bin/sh instead of /bin/bash in merssh wrapper scripts<commit_after>/****************************************************************************
**
** Copyright (C) 2012 - 2013 Jolla Ltd.
** Contact: http://jolla.com/
**
** This file is part of Qt Creator.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Digia.
**
****************************************************************************/

#include "mertarget.h"
#include "mersdk.h"
#include "merconstants.h"
#include "mersdkmanager.h"
#include "mertoolchain.h"
#include "merqtversion.h"
#include "mersdkkitinformation.h"
#include "mertargetkitinformation.h"
#include <debugger/debuggerkitinformation.h>
#include <qtsupport/qtversionmanager.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionfactory.h>
#include <projectexplorer/toolchainmanager.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <QDir>
#include <QStringList>

namespace Mer {
namespace Internal {

using namespace Mer::Constants;

const char* wrapperScripts[] =
{
    MER_WRAPPER_QMAKE,
    MER_WRAPPER_MAKE,
    MER_WRAPPER_GCC,
    MER_WRAPPER_DEPLOY,
    MER_WRAPPER_RPM,
    MER_WRAPPER_RPMBUILD
};

MerTarget::MerTarget(MerSdk* mersdk):
    m_sdk(mersdk),
    m_defaultGdb(QLatin1String(Constants::MER_DEBUGGER_FILENAME))  //TODO:
{
}

MerTarget::~MerTarget()
{
}

QString MerTarget::name() const
{
    return m_name;
}

void MerTarget::setName(const QString &name)
{
    m_name = name;
}

void MerTarget::setQmakeQuery(const QString &qmakeQuery)
{
    m_qmakeQuery = qmakeQuery;
}

void MerTarget::setGccDumpMachine(const QString &gccMachineDump)
{
    m_gccMachineDump = gccMachineDump;
}

void MerTarget::setDefaultGdb(const QString &name)
{
    m_defaultGdb=name;
}

bool MerTarget::fromMap(const QVariantMap &data)
{
    m_name = data.value(QLatin1String(Constants::MER_TARGET_NAME)).toString();
    m_qmakeQuery = data.value(QLatin1String(Constants::MER_TARGET_QMAKE_DUMP)).toString();
    m_gccMachineDump = data.value(QLatin1String(Constants::MER_TARGET_GCC_DUMP)).toString();
    m_defaultGdb = data.value(QLatin1String(Constants::MER_TARGET_DEFAULT_DEBUGGER)).toString();
    return isValid();
}

QVariantMap MerTarget::toMap() const
{
    QVariantMap result;
    result.insert(QLatin1String(Constants::MER_TARGET_NAME), m_name);
    result.insert(QLatin1String(Constants::MER_TARGET_QMAKE_DUMP), m_qmakeQuery);
    result.insert(QLatin1String(Constants::MER_TARGET_GCC_DUMP), m_gccMachineDump);
    result.insert(QLatin1String(Constants::MER_TARGET_DEFAULT_DEBUGGER), m_defaultGdb);
    return result;
}

bool MerTarget::isValid() const
{
    return !m_name.isEmpty() && !m_qmakeQuery.isEmpty() && !m_gccMachineDump.isEmpty();
}

QString MerTarget::targetPath() const
{
    return MerSdkManager::sdkToolsDirectory() + m_sdk->virtualMachineName() + QLatin1Char('/') + m_name;
}

bool MerTarget::createScripts() const
{
    if (!isValid())
        return false;

    const QString targetPath(this->targetPath());

    QDir targetDir(targetPath);
    if (!targetDir.exists() && !targetDir.mkpath(targetPath)) {
        qWarning() << "Could not create target directory." << targetDir;
        return false;
    }
    bool result = true;
    for (size_t i = 0; i < sizeof(wrapperScripts) / sizeof(wrapperScripts[0]); ++i)
         result &= createScript(targetPath, i);

    const QString qmakepath = targetPath + QLatin1Char('/') + QLatin1String(Constants::QMAKE_QUERY);
    const QString gccpath = targetPath + QLatin1Char('/') + QLatin1String(Constants::GCC_DUMPMACHINE);

    QString patchedQmakeQuery = m_qmakeQuery;
    patchedQmakeQuery.replace(QLatin1String(":/"),
                              QString::fromLatin1(":%1/%2/").arg(m_sdk->sharedTargetsPath()).arg(m_name));

    result &= createCacheFile(qmakepath, patchedQmakeQuery);
    result &= createCacheFile(gccpath, m_gccMachineDump);

    return result;
}

void MerTarget::deleteScripts() const
{
    Utils::FileUtils::removeRecursively(Utils::FileName::fromString(targetPath()));
}

ProjectExplorer::Kit* MerTarget::createKit() const
{
    if (!isValid())
        return 0;
    const QString sysroot(m_sdk->sharedTargetsPath() + QLatin1String("/") + m_name);

    Utils::FileName path = Utils::FileName::fromString(sysroot);
    if (!path.toFileInfo().exists()) {
        qWarning() << "Sysroot does not exist" << sysroot;
        return 0;
    }

    ProjectExplorer::Kit *k = new ProjectExplorer::Kit();
    k->setAutoDetected(true);
    k->setDisplayName(QString::fromLatin1("%1-%2").arg(m_sdk->virtualMachineName(), m_name));
    k->setIconPath(QLatin1String(Constants::MER_OPTIONS_CATEGORY_ICON));
    ProjectExplorer::SysRootKitInformation::setSysRoot(k, Utils::FileName::fromUserInput(sysroot));

    if (m_gccMachineDump.contains(QLatin1String("i486"))) {
        ProjectExplorer::DeviceTypeKitInformation::setDeviceTypeId(k, Constants::MER_DEVICE_TYPE_I486);
    } else if (m_gccMachineDump.contains(QLatin1String("arm"))) {
        ProjectExplorer::DeviceTypeKitInformation::setDeviceTypeId(k, Constants::MER_DEVICE_TYPE_ARM);
    }

    const QString gdb = Utils::HostOsInfo::withExecutableSuffix(m_defaultGdb);
    QString gdbDir = QCoreApplication::applicationDirPath();
    if (Utils::HostOsInfo::isMacHost()) {
        QDir dir = QDir(gdbDir);
        dir.cdUp();
        dir.cdUp();
        dir.cdUp();
        gdbDir = dir.path();
    }
    Utils::FileName gdbFileName = Utils::FileName::fromString(gdbDir + QLatin1String("/") + gdb);

    Debugger::DebuggerKitInformation::setEngineType(k,Debugger::GdbEngineType);
    Debugger::DebuggerKitInformation::setDebuggerCommand(k,gdbFileName);
    MerSdkKitInformation::setSdk(k,m_sdk);
    MerTargetKitInformation::setTargetName(k,name());
    return k;
}

MerQtVersion* MerTarget::createQtVersion() const
{
    const Utils::FileName qmake =
            Utils::FileName::fromString(targetPath() + QLatin1Char('/') +
                                        QLatin1String(Constants::MER_WRAPPER_QMAKE));
    QtSupport::QtVersionManager *qtvm = QtSupport::QtVersionManager::instance();
    // Is there a qtversion present for this qmake?
    QtSupport::BaseQtVersion *qtv = qtvm->qtVersionForQMakeBinary(qmake);
    if (qtv && !qtv->isValid()) {
        qtvm->removeVersion(qtv);
        qtv = 0;
    }
    if (!qtv)
        qtv = new MerQtVersion(qmake, true, targetPath());

    //QtSupport::QtVersionFactory::createQtVersionFromQMakePath(qmake, true, targetPath());

    QTC_ASSERT(qtv && qtv->type() == QLatin1String(Constants::MER_QT), return 0);

    MerQtVersion *merqtv = static_cast<MerQtVersion *>(qtv);
    const QString vmName = m_sdk->virtualMachineName();
    merqtv->setVirtualMachineName(vmName);
    merqtv->setTargetName(m_name);
    merqtv->setDisplayName(
                QString::fromLatin1("Qt %1 in %2 %3").arg(qtv->qtVersionString(),
                                                          vmName, m_name));
    return merqtv;
}

MerToolChain* MerTarget::createToolChain() const
{
    const Utils::FileName gcc =
            Utils::FileName::fromString(targetPath() + QLatin1Char('/') +
                                        QLatin1String(Constants::MER_WRAPPER_GCC));
    ProjectExplorer::ToolChainManager *tcm = ProjectExplorer::ToolChainManager::instance();
    QList<ProjectExplorer::ToolChain *> toolChains = tcm->toolChains();

    foreach (ProjectExplorer::ToolChain *tc, toolChains) {
        if (tc->compilerCommand() == gcc && tc->isAutoDetected()) {
            QTC_ASSERT(tc->type() == QLatin1String(Constants::MER_TOOLCHAIN_TYPE), return 0);
        }
    }

    MerToolChain* mertoolchain = new MerToolChain(true);
    const QString vmName = m_sdk->virtualMachineName();
    mertoolchain->setDisplayName(QString::fromLatin1("GCC (%1 %2)").arg(vmName, m_name));
    mertoolchain->setVirtualMachine(vmName);
    mertoolchain->setTargetName(m_name);
    mertoolchain->setCompilerCommand(gcc);
    return mertoolchain;
}

bool MerTarget::createScript(const QString &targetPath, int scriptIndex) const
{
    bool ok = false;
    const char* wrapperScriptCopy = wrapperScripts[scriptIndex];
    const QFile::Permissions rwrw = QFile::ReadOwner|QFile::ReadUser|QFile::ReadGroup
            |QFile::WriteOwner|QFile::WriteUser|QFile::WriteGroup;
    const QFile::Permissions rwxrwx = rwrw|QFile::ExeOwner|QFile::ExeUser|QFile::ExeGroup;

    QString wrapperScriptCommand = QLatin1String(wrapperScriptCopy);
    if (Utils::HostOsInfo::isWindowsHost())
        wrapperScriptCommand.chop(4); // remove the ".cmd"

    QString wrapperBinaryPath = QCoreApplication::applicationDirPath();
    if (Utils::HostOsInfo::isWindowsHost())
        wrapperBinaryPath += QLatin1String("/merssh.exe");
    else if (Utils::HostOsInfo::isMacHost())
        wrapperBinaryPath += QLatin1String("/../Resources/merssh");
    else
        wrapperBinaryPath += QLatin1String("/merssh");

    using namespace Utils;
    const QString scriptCopyPath = targetPath + QLatin1Char('/') + QLatin1String(wrapperScriptCopy);
    QDir targetDir(targetPath);
    const QString targetName = targetDir.dirName();
    targetDir.cdUp();
    const QString merDevToolsDir = targetDir.canonicalPath();
    QFile script(scriptCopyPath);
    ok = script.open(QIODevice::WriteOnly);
    if (!ok) {
        qWarning() << "Could not open script" << scriptCopyPath;
        return false;
    }

    QString scriptContent;

    if (HostOsInfo::isWindowsHost()) {
        scriptContent += QLatin1String("@echo off\nSetLocal EnableDelayedExpansion\n");
        scriptContent += QLatin1String("set ARGUMENTS=\nFOR %%a IN (%*) DO set ARGUMENTS=!ARGUMENTS! ^ '%%a'\n");
        scriptContent += QLatin1String("set ") +
                QLatin1String(Mer::Constants::MER_SSH_TARGET_NAME) +
                QLatin1Char('=') + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("set ") +
                QLatin1String(Mer::Constants::MER_SSH_SDK_TOOLS) +
                QLatin1Char('=') + merDevToolsDir + QDir::separator() + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("SetLocal DisableDelayedExpansion\n");
        scriptContent += QLatin1Char('"') +
                QDir::toNativeSeparators(wrapperBinaryPath) + QLatin1String("\" ") +
                wrapperScriptCommand + QLatin1Char(' ') + QLatin1String("%ARGUMENTS%\n");
    }

    if (HostOsInfo::isAnyUnixHost()) {
        scriptContent += QLatin1String("#!/bin/sh\n");
        scriptContent += QLatin1String("ARGUMENTS=\"\";for ARGUMENT in \"$@\"; do ARGUMENTS=\"${ARGUMENTS} '${ARGUMENT}'\" ; done;\n");
        scriptContent += QLatin1String("export  ") +
                QLatin1String(Mer::Constants::MER_SSH_TARGET_NAME) +
                QLatin1Char('=') + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("export  ") +
                QLatin1String(Mer::Constants::MER_SSH_SDK_TOOLS) +
                QLatin1Char('=') + merDevToolsDir + QDir::separator() + targetName + QLatin1Char('\n');
        scriptContent += QLatin1String("exec ");
        scriptContent += QLatin1Char('"') +
                QDir::toNativeSeparators(wrapperBinaryPath) + QLatin1String("\" ") +
                wrapperScriptCommand + QLatin1Char(' ') + QLatin1String("${ARGUMENTS}");
    }

    ok = script.write(scriptContent.toUtf8()) != -1;
    if (!ok) {
        qWarning() << "Could not write script" << scriptCopyPath;
        return false;
    }

    ok = QFile::setPermissions(scriptCopyPath, rwxrwx);
    if (!ok) {
        qWarning() << "Could not set file permissions on script" << scriptCopyPath;
        return false;
    }
    return ok;
}

bool MerTarget::createCacheFile(const QString &fileName, const QString &content) const
{
    bool ok = false;

    QFile file(fileName);
    ok = file.open(QIODevice::WriteOnly);
    if (!ok) {
        qWarning() << "Could not open cache file" << fileName;
        return false;
    }

    ok = file.write(content.toUtf8()) != -1;
    if (!ok) {
        qWarning() << "Could not write cache file " << fileName;
        return false;
    }
    file.close();
    return ok;
}

bool MerTarget::operator==(const MerTarget &other) const
{
    return m_name == other.m_name && m_qmakeQuery == other.m_qmakeQuery && m_gccMachineDump == other.m_gccMachineDump;
}

}
}
<|endoftext|>


<commit_before>//==============================================================================
// Plugin manager
//==============================================================================

#include "plugin.h"
#include "coreinterface.h"
#include "pluginmanager.h"

//==============================================================================

#include <QApplication>
#include <QDir>

//==============================================================================

namespace OpenCOR {

//==============================================================================

PluginManager::PluginManager(QSettings *pSettings,
                             const PluginInfo::Type &pGuiOrConsoleType) :
    mSettings(pSettings),
    mVersion(PluginInfo::V001),
    mGuiOrConsoleType(pGuiOrConsoleType),
    mPlugins(Plugins())
{
    mPluginsDir =  QDir(qApp->applicationDirPath()).canonicalPath()
                  +QDir::separator()+QString("..")
#ifndef Q_WS_MAC
                  +QDir::separator()+"plugins"
#else
                  +QDir::separator()+"PlugIns"
#endif
                  +QDir::separator()+qApp->applicationName();

#ifndef Q_WS_MAC
    // The plugins directory should be correct, but in case we try to run
    // OpenCOR on Windows or Linux AND from within Qt Creator, then the binary
    // will be running from [OpenCOR]/build/OpenCOR[.exe] rather than
    // [OpenCOR]/build/bin/OpenCOR[.exe] as it should if we were to mimic the
    // case where OpenCOR has been deployed. Then, because the plugins are in
    // [OpenCOR]/build/plugins/OpenCOR, we must skip the "../" bit. Yes, it's
    // not neat, but... is there another solution?...

    if (!QDir(mPluginsDir).exists())
        mPluginsDir =  QDir(qApp->applicationDirPath()).canonicalPath()
                      +QDir::separator()+"plugins"
                      +QDir::separator()+qApp->applicationName();
#endif

    mPluginsDir = QDir::toNativeSeparators(mPluginsDir);

    // Retrieve the list of plugins available for loading

    QFileInfoList fileInfoList = QDir(mPluginsDir).entryInfoList(QStringList("*"+PluginExtension),
                                                                 QDir::Files);

    QStringList fileNames;

    foreach (const QFileInfo &file, fileInfoList)
        fileNames << QDir::toNativeSeparators(file.canonicalFilePath());

    // Unmanageable plugins (e.g. the QScintilla plugin) don't, by default, get
    // loaded, but the situation is obviously different if such a plugin is
    // required by another plugin (e.g. the Viewer plugin requires the
    // QtMmlWidget plugin), in which case the unmanageable plugin must be
    // loaded. So, we must here determine which of those plugins must be
    // loaded...

    QStringList plugins;

    foreach (const QString &fileName, fileNames)
        if (   Plugin::info(fileName).manageable()
            && Plugin::load(Plugin::name(fileName)))
            // The plugin is manageable and to be loaded, so retrieve its
            // dependencies

            plugins << Plugin::requiredPlugins(mPluginsDir,
                                               Plugin::name(fileName));

    plugins.removeDuplicates();

    // Knowing which plugins are required, we must now ensure that these are
    // loaded first. Note that this is not required on Windows (even though it
    // clearly doesn't harm having them loaded first!), but on Linux and Mac OS
    // X it certainly is since otherwise the plugin's status will be wrong
    // (indeed, on those systems, we check that dependencies are first loaded
    // before loading the plugin itself), so...

    QStringList orderedFileNames;

    foreach (const QString &plugin, plugins)
        orderedFileNames << Plugin::fileName(mPluginsDir, plugin);

    orderedFileNames << fileNames;

    orderedFileNames.removeDuplicates();

    // Deal with all the plugins we found

    foreach (const QString &fileName, orderedFileNames)
        mPlugins << new Plugin(fileName, mGuiOrConsoleType,
                               plugins.contains(Plugin::name(fileName)),
                               version(), pluginsDir()
#ifndef Q_WS_WIN
                               , this
#endif
                              );
}

//==============================================================================

PluginManager::~PluginManager()
{
    // Delete all of the plugins

    foreach (Plugin *plugin, mPlugins)
        delete plugin;
}

//==============================================================================

Plugins PluginManager::plugins() const
{
    // Return a list of all our plugins, whether loaded or not

    return mPlugins;
}

//==============================================================================

Plugins PluginManager::loadedPlugins() const
{
    // Return a list of only our loaded plugins

    Plugins res = Plugins();

    foreach (Plugin *plugin, mPlugins)
        if (plugin->status() == Plugin::Loaded)
            res << plugin;

    return res;
}

//==============================================================================

QString PluginManager::pluginsDir() const
{
    // Return the plugins directory

    return mPluginsDir;
}

//==============================================================================

Plugin * PluginManager::plugin(const QString &pName) const
{
    // Return the plugin which name is the one we have been passed

    foreach (Plugin *plugin, mPlugins)
        if (!pName.compare(plugin->name()))
            // This is the plugin we are after, so...

            return plugin;

    // The plugin we are after wasn't found, so...

    return 0;
}

//==============================================================================

PluginInfo::Version PluginManager::version() const
{
    // Return the version used by the plugin manager

    return mVersion;
}

//==============================================================================

QSettings * PluginManager::settings() const
{
    // Return the settings object

    return mSettings;
}

//==============================================================================

}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Fixed an issue with the loading of plugins following our recent improvement (see commit 5b85057cebc0c99aa384e65f2ec1b4dbf3fc8981).<commit_after>//==============================================================================
// Plugin manager
//==============================================================================

#include "plugin.h"
#include "coreinterface.h"
#include "pluginmanager.h"

//==============================================================================

#include <QApplication>
#include <QDir>

//==============================================================================

namespace OpenCOR {

//==============================================================================

PluginManager::PluginManager(QSettings *pSettings,
                             const PluginInfo::Type &pGuiOrConsoleType) :
    mSettings(pSettings),
    mVersion(PluginInfo::V001),
    mGuiOrConsoleType(pGuiOrConsoleType),
    mPlugins(Plugins())
{
    mPluginsDir =  QDir(qApp->applicationDirPath()).canonicalPath()
                  +QDir::separator()+QString("..")
#ifndef Q_WS_MAC
                  +QDir::separator()+"plugins"
#else
                  +QDir::separator()+"PlugIns"
#endif
                  +QDir::separator()+qApp->applicationName();

#ifndef Q_WS_MAC
    // The plugins directory should be correct, but in case we try to run
    // OpenCOR on Windows or Linux AND from within Qt Creator, then the binary
    // will be running from [OpenCOR]/build/OpenCOR[.exe] rather than
    // [OpenCOR]/build/bin/OpenCOR[.exe] as it should if we were to mimic the
    // case where OpenCOR has been deployed. Then, because the plugins are in
    // [OpenCOR]/build/plugins/OpenCOR, we must skip the "../" bit. Yes, it's
    // not neat, but... is there another solution?...

    if (!QDir(mPluginsDir).exists())
        mPluginsDir =  QDir(qApp->applicationDirPath()).canonicalPath()
                      +QDir::separator()+"plugins"
                      +QDir::separator()+qApp->applicationName();
#endif

    mPluginsDir = QDir::toNativeSeparators(QDir(mPluginsDir).canonicalPath());

    // Retrieve the list of plugins available for loading

    QFileInfoList fileInfoList = QDir(mPluginsDir).entryInfoList(QStringList("*"+PluginExtension),
                                                                 QDir::Files);

    QStringList fileNames;

    foreach (const QFileInfo &file, fileInfoList)
        fileNames << QDir::toNativeSeparators(file.canonicalFilePath());

    // Unmanageable plugins (e.g. the QScintilla plugin) don't, by default, get
    // loaded, but the situation is obviously different if such a plugin is
    // required by another plugin (e.g. the Viewer plugin requires the
    // QtMmlWidget plugin), in which case the unmanageable plugin must be
    // loaded. So, we must here determine which of those plugins must be
    // loaded...

    QStringList plugins;

    foreach (const QString &fileName, fileNames)
        if (   Plugin::info(fileName).manageable()
            && Plugin::load(Plugin::name(fileName)))
            // The plugin is manageable and to be loaded, so retrieve its
            // dependencies

            plugins << Plugin::requiredPlugins(mPluginsDir,
                                               Plugin::name(fileName));

    plugins.removeDuplicates();

    // Knowing which plugins are required, we must now ensure that these are
    // loaded first. Note that this is not required on Windows (even though it
    // clearly doesn't harm having them loaded first!), but on Linux and Mac OS
    // X it certainly is since otherwise the plugin's status will be wrong
    // (indeed, on those systems, we check that dependencies are first loaded
    // before loading the plugin itself), so...

    QStringList orderedFileNames;

    foreach (const QString &plugin, plugins)
        orderedFileNames << Plugin::fileName(mPluginsDir, plugin);

    orderedFileNames << fileNames;

    orderedFileNames.removeDuplicates();

    // Deal with all the plugins we found

    foreach (const QString &fileName, orderedFileNames)
        mPlugins << new Plugin(fileName, mGuiOrConsoleType,
                               plugins.contains(Plugin::name(fileName)),
                               version(), pluginsDir()
#ifndef Q_WS_WIN
                               , this
#endif
                              );
}

//==============================================================================

PluginManager::~PluginManager()
{
    // Delete all of the plugins

    foreach (Plugin *plugin, mPlugins)
        delete plugin;
}

//==============================================================================

Plugins PluginManager::plugins() const
{
    // Return a list of all our plugins, whether loaded or not

    return mPlugins;
}

//==============================================================================

Plugins PluginManager::loadedPlugins() const
{
    // Return a list of only our loaded plugins

    Plugins res = Plugins();

    foreach (Plugin *plugin, mPlugins)
        if (plugin->status() == Plugin::Loaded)
            res << plugin;

    return res;
}

//==============================================================================

QString PluginManager::pluginsDir() const
{
    // Return the plugins directory

    return mPluginsDir;
}

//==============================================================================

Plugin * PluginManager::plugin(const QString &pName) const
{
    // Return the plugin which name is the one we have been passed

    foreach (Plugin *plugin, mPlugins)
        if (!pName.compare(plugin->name()))
            // This is the plugin we are after, so...

            return plugin;

    // The plugin we are after wasn't found, so...

    return 0;
}

//==============================================================================

PluginInfo::Version PluginManager::version() const
{
    // Return the version used by the plugin manager

    return mVersion;
}

//==============================================================================

QSettings * PluginManager::settings() const
{
    // Return the settings object

    return mSettings;
}

//==============================================================================

}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>