<commit_before>/*
 * Copyright (C) 2008-2012 The QXmpp developers
 *
 * Author:
 *  Manjeet Dahiya
 *
 * Source:
 *  http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include "QXmppPresence.h"
#include "QXmppUtils.h"
#include <QtDebug>
#include <QDomElement>
#include <QXmlStreamWriter>
#include "QXmppConstants.h"

static const char* presence_types[] = {
    "error",
    "",
    "unavailable",
    "subscribe",
    "subscribed",
    "unsubscribe",
    "unsubscribed",
    "probe"
};

static const char* presence_shows[] = {
    "",
    "away",
    "xa",
    "dnd",
    "chat",
    "invisible"
};

class QXmppPresencePrivate : public QSharedData
{
public:
    QXmppPresence::AvailableStatusType availableStatusType;
    QXmppPresence::Type type;
    QXmppPresence::Status status;

    /// XEP-0153: vCard-Based Avatars

    /// photoHash: the SHA1 hash of the avatar image data itself (not the base64-encoded version)
    /// in accordance with RFC 3174
    QByteArray photoHash;
    QXmppPresence::VCardUpdateType vCardUpdateType;

    // XEP-0115: Entity Capabilities
    QString capabilityHash;
    QString capabilityNode;
    QByteArray capabilityVer;
    // Legacy XEP-0115: Entity Capabilities
    QStringList capabilityExt;

    // XEP-0045: Multi-User Chat
    QXmppMucItem mucItem;
    QList<int> mucStatusCodes;
};

/// Constructs a QXmppPresence.
///
/// \param type

QXmppPresence::QXmppPresence(QXmppPresence::Type type)
    : d(new QXmppPresencePrivate)
{
    d->type = type;
    d->vCardUpdateType = VCardUpdateNone;
}

/// Constructs a copy of \a other.

QXmppPresence::QXmppPresence(const QXmppPresence &other)
    : QXmppStanza(other)
    , d(other.d)
{
}

/// Destroys a QXmppPresence.

QXmppPresence::~QXmppPresence()
{

}

/// Assigns \a other to this presence.

QXmppPresence &QXmppPresence::operator=(const QXmppPresence &other)
{
    QXmppStanza::operator=(other);
    d = other.d;
    return *this;
}

/// Returns the availability status type, for instance busy or away.
///
/// This will not tell you whether a contact is connected, check whether
/// type() is QXmppPresence::Available instead.

QXmppPresence::AvailableStatusType QXmppPresence::availableStatusType() const
{
    return static_cast<QXmppPresence::AvailableStatusType>(d->status.type());
}

/// Sets the availability status type, for instance busy or away.

void QXmppPresence::setAvailableStatusType(AvailableStatusType type)
{
    d->status.setType(static_cast<QXmppPresence::Status::Type>(type));
}

/// Returns the priority level of the resource.

int QXmppPresence::priority() const
{
    return d->status.priority();
}

/// Sets the \a priority level of the resource.

void QXmppPresence::setPriority(int priority)
{
    d->status.setPriority(priority);
}

/// Returns the status text, a textual description of the user's status.

QString QXmppPresence::statusText() const
{
    return d->status.statusText();
}

/// Sets the status text, a textual description of the user's status.
///
/// \param statusText The status text, for example "Gone fishing".

void QXmppPresence::setStatusText(const QString& statusText)
{
    d->status.setStatusText(statusText);
}

/// Returns the presence type.
///
/// You can use this method to determine the action which needs to be
/// taken in response to receiving the presence. For instance, if the type is
/// QXmppPresence::Available or QXmppPresence::Unavailable, you could update
/// the icon representing a contact's availability.

QXmppPresence::Type QXmppPresence::type() const
{
    return d->type;
}

/// Sets the presence type.
///
/// \param type

void QXmppPresence::setType(QXmppPresence::Type type)
{
    d->type = type;
}

/// \cond
void QXmppPresence::parse(const QDomElement &element)
{
    QXmppStanza::parse(element);

    const QString type = element.attribute("type");
    for (int i = Error; i <= Probe; i++) {
        if (type == presence_types[i]) {
            d->type = static_cast<Type>(i);
            break;
        }
    }
    d->status.parse(element);

    QXmppElementList extensions;
    QDomElement xElement = element.firstChildElement();
    d->vCardUpdateType = VCardUpdateNone;
    while(!xElement.isNull())
    {
        // XEP-0045: Multi-User Chat
        if(xElement.namespaceURI() == ns_muc_user)
        {
            QDomElement itemElement = xElement.firstChildElement("item");
            d->mucItem.parse(itemElement);
            QDomElement statusElement = xElement.firstChildElement("status");
            d->mucStatusCodes.clear();
            while (!statusElement.isNull()) {
                d->mucStatusCodes << statusElement.attribute("code").toInt();
                statusElement = statusElement.nextSiblingElement("status");
            }
        }
        // XEP-0153: vCard-Based Avatars
        else if(xElement.namespaceURI() == ns_vcard_update)
        {
            QDomElement photoElement = xElement.firstChildElement("photo");
            if(!photoElement.isNull())
            {
                d->photoHash = QByteArray::fromHex(photoElement.text().toAscii());
                if(d->photoHash.isEmpty())
                    d->vCardUpdateType = VCardUpdateNoPhoto;
                else
                    d->vCardUpdateType = VCardUpdateValidPhoto;
            }
            else
            {
                d->photoHash = QByteArray();
                d->vCardUpdateType = VCardUpdateNotReady;
            }
        }
        // XEP-0115: Entity Capabilities
        else if(xElement.tagName() == "c" && xElement.namespaceURI() == ns_capabilities)
        {
            d->capabilityNode = xElement.attribute("node");
            d->capabilityVer = QByteArray::fromBase64(xElement.attribute("ver").toAscii());
            d->capabilityHash = xElement.attribute("hash");
            d->capabilityExt = xElement.attribute("ext").split(" ", QString::SkipEmptyParts);
        }
        else if (xElement.tagName() == "error")
        {
        }
        else if (xElement.tagName() == "show")
        {
        }
        else if (xElement.tagName() == "status")
        {
        }
        else if (xElement.tagName() == "priority")
        {
        }
        else
        {
            // other extensions
            extensions << QXmppElement(xElement);
        }
        xElement = xElement.nextSiblingElement();
    }
    setExtensions(extensions);
}

void QXmppPresence::toXml(QXmlStreamWriter *xmlWriter) const
{
    xmlWriter->writeStartElement("presence");
    helperToXmlAddAttribute(xmlWriter,"xml:lang", lang());
    helperToXmlAddAttribute(xmlWriter,"id", id());
    helperToXmlAddAttribute(xmlWriter,"to", to());
    helperToXmlAddAttribute(xmlWriter,"from", from());
    helperToXmlAddAttribute(xmlWriter,"type", presence_types[d->type]);
    d->status.toXml(xmlWriter);

    error().toXml(xmlWriter);

    // XEP-0045: Multi-User Chat
    if(!d->mucItem.isNull() || !d->mucStatusCodes.isEmpty())
    {
        xmlWriter->writeStartElement("x");
        xmlWriter->writeAttribute("xmlns", ns_muc_user);
        if (!d->mucItem.isNull())
            d->mucItem.toXml(xmlWriter);
        foreach (int code, d->mucStatusCodes) {
            xmlWriter->writeStartElement("status");
            xmlWriter->writeAttribute("code", QString::number(code));
            xmlWriter->writeEndElement();
        }
        xmlWriter->writeEndElement();
    }

    // XEP-0153: vCard-Based Avatars
    if(d->vCardUpdateType != VCardUpdateNone)
    {
        xmlWriter->writeStartElement("x");
        xmlWriter->writeAttribute("xmlns", ns_vcard_update);
        switch(d->vCardUpdateType)
        {
        case VCardUpdateNoPhoto:
            helperToXmlAddTextElement(xmlWriter, "photo", "");
            break;
        case VCardUpdateValidPhoto:
            helperToXmlAddTextElement(xmlWriter, "photo", d->photoHash.toHex());
            break;
        case VCardUpdateNotReady:
            break;
        default:
            break;
        }
        xmlWriter->writeEndElement();
    }

    if(!d->capabilityNode.isEmpty() && !d->capabilityVer.isEmpty()
        && !d->capabilityHash.isEmpty())
    {
        xmlWriter->writeStartElement("c");
        xmlWriter->writeAttribute("xmlns", ns_capabilities);
        helperToXmlAddAttribute(xmlWriter, "hash", d->capabilityHash);
        helperToXmlAddAttribute(xmlWriter, "node", d->capabilityNode);
        helperToXmlAddAttribute(xmlWriter, "ver", d->capabilityVer.toBase64());
        xmlWriter->writeEndElement();
    }

    // other extensions
    QXmppStanza::extensionsToXml(xmlWriter);

    xmlWriter->writeEndElement();
}
/// \endcond

/// Returns the photo-hash of the VCardUpdate.
///
/// \return QByteArray

QByteArray QXmppPresence::photoHash() const
{
    return d->photoHash;
}

/// Sets the photo-hash of the VCardUpdate.
///
/// \param photoHash as QByteArray

void QXmppPresence::setPhotoHash(const QByteArray& photoHash)
{
    d->photoHash = photoHash;
}

/// Returns the type of VCardUpdate
///
/// \return VCardUpdateType

QXmppPresence::VCardUpdateType QXmppPresence::vCardUpdateType() const
{
    return d->vCardUpdateType;
}

/// Sets the type of VCardUpdate
///
/// \param type VCardUpdateType

void QXmppPresence::setVCardUpdateType(VCardUpdateType type)
{
    d->vCardUpdateType = type;
}

/// XEP-0115: Entity Capabilities
QString QXmppPresence::capabilityHash() const
{
    return d->capabilityHash;
}

/// XEP-0115: Entity Capabilities
void QXmppPresence::setCapabilityHash(const QString& hash)
{
    d->capabilityHash = hash;
}

/// XEP-0115: Entity Capabilities
QString QXmppPresence::capabilityNode() const
{
    return d->capabilityNode;
}

/// XEP-0115: Entity Capabilities
void QXmppPresence::setCapabilityNode(const QString& node)
{
    d->capabilityNode = node;
}

/// XEP-0115: Entity Capabilities
QByteArray QXmppPresence::capabilityVer() const
{
    return d->capabilityVer;
}

/// XEP-0115: Entity Capabilities
void QXmppPresence::setCapabilityVer(const QByteArray& ver)
{
    d->capabilityVer = ver;
}

/// Legacy XEP-0115: Entity Capabilities
QStringList QXmppPresence::capabilityExt() const
{
    return d->capabilityExt;
}

/// Returns the MUC item.

QXmppMucItem QXmppPresence::mucItem() const
{
    return d->mucItem;
}

/// Sets the MUC item.
///
/// \param item

void QXmppPresence::setMucItem(const QXmppMucItem &item)
{
    d->mucItem = item;
}

/// Returns the MUC status codes.

QList<int> QXmppPresence::mucStatusCodes() const
{
    return d->mucStatusCodes;
}

/// Sets the MUC status codes.
///
/// \param codes

void QXmppPresence::setMucStatusCodes(const QList<int> &codes)
{
    d->mucStatusCodes = codes;
}

/// \cond
const QXmppPresence::Status& QXmppPresence::status() const
{
    return d->status;
}

QXmppPresence::Status& QXmppPresence::status()
{
    return d->status;
}

void QXmppPresence::setStatus(const QXmppPresence::Status& status)
{
    d->status = status;
}

QXmppPresence::Status::Status(QXmppPresence::Status::Type type,
                             const QString statusText, int priority) :
                                m_type(type),
                                m_statusText(statusText), m_priority(priority)
{
}

QXmppPresence::Status::Type QXmppPresence::Status::type() const
{
    return m_type;
}

void QXmppPresence::Status::setType(QXmppPresence::Status::Type type)
{
    m_type = type;
}

QString QXmppPresence::Status::statusText() const
{
    return m_statusText;
}

void QXmppPresence::Status::setStatusText(const QString& str)
{
    m_statusText = str;
}

int QXmppPresence::Status::priority() const
{
    return m_priority;
}

void QXmppPresence::Status::setPriority(int priority)
{
    m_priority = priority;
}

void QXmppPresence::Status::parse(const QDomElement &element)
{
    const QString show = element.firstChildElement("show").text();
    for (int i = Online; i <= Invisible; i++) {
        if (show == presence_shows[i]) {
            m_type = static_cast<Type>(i);
            break;
        }
    }
    m_statusText = element.firstChildElement("status").text();
    m_priority = element.firstChildElement("priority").text().toInt();
}

void QXmppPresence::Status::toXml(QXmlStreamWriter *xmlWriter) const
{
    const QString show = presence_shows[m_type];
    if (!show.isEmpty())
        helperToXmlAddTextElement(xmlWriter, "show", show);
    if (!m_statusText.isEmpty())
        helperToXmlAddTextElement(xmlWriter, "status", m_statusText);
    if (m_priority != 0)
        helperToXmlAddTextElement(xmlWriter, "priority", QString::number(m_priority));
}
/// \endcond
<commit_msg>exclude "addresses" from presence extensions<commit_after>/*
 * Copyright (C) 2008-2012 The QXmpp developers
 *
 * Author:
 *  Manjeet Dahiya
 *
 * Source:
 *  http://code.google.com/p/qxmpp
 *
 * This file is a part of QXmpp library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */

#include "QXmppPresence.h"
#include "QXmppUtils.h"
#include <QtDebug>
#include <QDomElement>
#include <QXmlStreamWriter>
#include "QXmppConstants.h"

static const char* presence_types[] = {
    "error",
    "",
    "unavailable",
    "subscribe",
    "subscribed",
    "unsubscribe",
    "unsubscribed",
    "probe"
};

static const char* presence_shows[] = {
    "",
    "away",
    "xa",
    "dnd",
    "chat",
    "invisible"
};

class QXmppPresencePrivate : public QSharedData
{
public:
    QXmppPresence::AvailableStatusType availableStatusType;
    QXmppPresence::Type type;
    QXmppPresence::Status status;

    /// XEP-0153: vCard-Based Avatars

    /// photoHash: the SHA1 hash of the avatar image data itself (not the base64-encoded version)
    /// in accordance with RFC 3174
    QByteArray photoHash;
    QXmppPresence::VCardUpdateType vCardUpdateType;

    // XEP-0115: Entity Capabilities
    QString capabilityHash;
    QString capabilityNode;
    QByteArray capabilityVer;
    // Legacy XEP-0115: Entity Capabilities
    QStringList capabilityExt;

    // XEP-0045: Multi-User Chat
    QXmppMucItem mucItem;
    QList<int> mucStatusCodes;
};

/// Constructs a QXmppPresence.
///
/// \param type

QXmppPresence::QXmppPresence(QXmppPresence::Type type)
    : d(new QXmppPresencePrivate)
{
    d->type = type;
    d->vCardUpdateType = VCardUpdateNone;
}

/// Constructs a copy of \a other.

QXmppPresence::QXmppPresence(const QXmppPresence &other)
    : QXmppStanza(other)
    , d(other.d)
{
}

/// Destroys a QXmppPresence.

QXmppPresence::~QXmppPresence()
{

}

/// Assigns \a other to this presence.

QXmppPresence &QXmppPresence::operator=(const QXmppPresence &other)
{
    QXmppStanza::operator=(other);
    d = other.d;
    return *this;
}

/// Returns the availability status type, for instance busy or away.
///
/// This will not tell you whether a contact is connected, check whether
/// type() is QXmppPresence::Available instead.

QXmppPresence::AvailableStatusType QXmppPresence::availableStatusType() const
{
    return static_cast<QXmppPresence::AvailableStatusType>(d->status.type());
}

/// Sets the availability status type, for instance busy or away.

void QXmppPresence::setAvailableStatusType(AvailableStatusType type)
{
    d->status.setType(static_cast<QXmppPresence::Status::Type>(type));
}

/// Returns the priority level of the resource.

int QXmppPresence::priority() const
{
    return d->status.priority();
}

/// Sets the \a priority level of the resource.

void QXmppPresence::setPriority(int priority)
{
    d->status.setPriority(priority);
}

/// Returns the status text, a textual description of the user's status.

QString QXmppPresence::statusText() const
{
    return d->status.statusText();
}

/// Sets the status text, a textual description of the user's status.
///
/// \param statusText The status text, for example "Gone fishing".

void QXmppPresence::setStatusText(const QString& statusText)
{
    d->status.setStatusText(statusText);
}

/// Returns the presence type.
///
/// You can use this method to determine the action which needs to be
/// taken in response to receiving the presence. For instance, if the type is
/// QXmppPresence::Available or QXmppPresence::Unavailable, you could update
/// the icon representing a contact's availability.

QXmppPresence::Type QXmppPresence::type() const
{
    return d->type;
}

/// Sets the presence type.
///
/// \param type

void QXmppPresence::setType(QXmppPresence::Type type)
{
    d->type = type;
}

/// \cond
void QXmppPresence::parse(const QDomElement &element)
{
    QXmppStanza::parse(element);

    const QString type = element.attribute("type");
    for (int i = Error; i <= Probe; i++) {
        if (type == presence_types[i]) {
            d->type = static_cast<Type>(i);
            break;
        }
    }
    d->status.parse(element);

    QXmppElementList extensions;
    QDomElement xElement = element.firstChildElement();
    d->vCardUpdateType = VCardUpdateNone;
    while(!xElement.isNull())
    {
        // XEP-0045: Multi-User Chat
        if(xElement.namespaceURI() == ns_muc_user)
        {
            QDomElement itemElement = xElement.firstChildElement("item");
            d->mucItem.parse(itemElement);
            QDomElement statusElement = xElement.firstChildElement("status");
            d->mucStatusCodes.clear();
            while (!statusElement.isNull()) {
                d->mucStatusCodes << statusElement.attribute("code").toInt();
                statusElement = statusElement.nextSiblingElement("status");
            }
        }
        // XEP-0153: vCard-Based Avatars
        else if(xElement.namespaceURI() == ns_vcard_update)
        {
            QDomElement photoElement = xElement.firstChildElement("photo");
            if(!photoElement.isNull())
            {
                d->photoHash = QByteArray::fromHex(photoElement.text().toAscii());
                if(d->photoHash.isEmpty())
                    d->vCardUpdateType = VCardUpdateNoPhoto;
                else
                    d->vCardUpdateType = VCardUpdateValidPhoto;
            }
            else
            {
                d->photoHash = QByteArray();
                d->vCardUpdateType = VCardUpdateNotReady;
            }
        }
        // XEP-0115: Entity Capabilities
        else if(xElement.tagName() == "c" && xElement.namespaceURI() == ns_capabilities)
        {
            d->capabilityNode = xElement.attribute("node");
            d->capabilityVer = QByteArray::fromBase64(xElement.attribute("ver").toAscii());
            d->capabilityHash = xElement.attribute("hash");
            d->capabilityExt = xElement.attribute("ext").split(" ", QString::SkipEmptyParts);
        }
        else if (xElement.tagName() == "addresses")
        {
        }
        else if (xElement.tagName() == "error")
        {
        }
        else if (xElement.tagName() == "show")
        {
        }
        else if (xElement.tagName() == "status")
        {
        }
        else if (xElement.tagName() == "priority")
        {
        }
        else
        {
            // other extensions
            extensions << QXmppElement(xElement);
        }
        xElement = xElement.nextSiblingElement();
    }
    setExtensions(extensions);
}

void QXmppPresence::toXml(QXmlStreamWriter *xmlWriter) const
{
    xmlWriter->writeStartElement("presence");
    helperToXmlAddAttribute(xmlWriter,"xml:lang", lang());
    helperToXmlAddAttribute(xmlWriter,"id", id());
    helperToXmlAddAttribute(xmlWriter,"to", to());
    helperToXmlAddAttribute(xmlWriter,"from", from());
    helperToXmlAddAttribute(xmlWriter,"type", presence_types[d->type]);
    d->status.toXml(xmlWriter);

    error().toXml(xmlWriter);

    // XEP-0045: Multi-User Chat
    if(!d->mucItem.isNull() || !d->mucStatusCodes.isEmpty())
    {
        xmlWriter->writeStartElement("x");
        xmlWriter->writeAttribute("xmlns", ns_muc_user);
        if (!d->mucItem.isNull())
            d->mucItem.toXml(xmlWriter);
        foreach (int code, d->mucStatusCodes) {
            xmlWriter->writeStartElement("status");
            xmlWriter->writeAttribute("code", QString::number(code));
            xmlWriter->writeEndElement();
        }
        xmlWriter->writeEndElement();
    }

    // XEP-0153: vCard-Based Avatars
    if(d->vCardUpdateType != VCardUpdateNone)
    {
        xmlWriter->writeStartElement("x");
        xmlWriter->writeAttribute("xmlns", ns_vcard_update);
        switch(d->vCardUpdateType)
        {
        case VCardUpdateNoPhoto:
            helperToXmlAddTextElement(xmlWriter, "photo", "");
            break;
        case VCardUpdateValidPhoto:
            helperToXmlAddTextElement(xmlWriter, "photo", d->photoHash.toHex());
            break;
        case VCardUpdateNotReady:
            break;
        default:
            break;
        }
        xmlWriter->writeEndElement();
    }

    if(!d->capabilityNode.isEmpty() && !d->capabilityVer.isEmpty()
        && !d->capabilityHash.isEmpty())
    {
        xmlWriter->writeStartElement("c");
        xmlWriter->writeAttribute("xmlns", ns_capabilities);
        helperToXmlAddAttribute(xmlWriter, "hash", d->capabilityHash);
        helperToXmlAddAttribute(xmlWriter, "node", d->capabilityNode);
        helperToXmlAddAttribute(xmlWriter, "ver", d->capabilityVer.toBase64());
        xmlWriter->writeEndElement();
    }

    // other extensions
    QXmppStanza::extensionsToXml(xmlWriter);

    xmlWriter->writeEndElement();
}
/// \endcond

/// Returns the photo-hash of the VCardUpdate.
///
/// \return QByteArray

QByteArray QXmppPresence::photoHash() const
{
    return d->photoHash;
}

/// Sets the photo-hash of the VCardUpdate.
///
/// \param photoHash as QByteArray

void QXmppPresence::setPhotoHash(const QByteArray& photoHash)
{
    d->photoHash = photoHash;
}

/// Returns the type of VCardUpdate
///
/// \return VCardUpdateType

QXmppPresence::VCardUpdateType QXmppPresence::vCardUpdateType() const
{
    return d->vCardUpdateType;
}

/// Sets the type of VCardUpdate
///
/// \param type VCardUpdateType

void QXmppPresence::setVCardUpdateType(VCardUpdateType type)
{
    d->vCardUpdateType = type;
}

/// XEP-0115: Entity Capabilities
QString QXmppPresence::capabilityHash() const
{
    return d->capabilityHash;
}

/// XEP-0115: Entity Capabilities
void QXmppPresence::setCapabilityHash(const QString& hash)
{
    d->capabilityHash = hash;
}

/// XEP-0115: Entity Capabilities
QString QXmppPresence::capabilityNode() const
{
    return d->capabilityNode;
}

/// XEP-0115: Entity Capabilities
void QXmppPresence::setCapabilityNode(const QString& node)
{
    d->capabilityNode = node;
}

/// XEP-0115: Entity Capabilities
QByteArray QXmppPresence::capabilityVer() const
{
    return d->capabilityVer;
}

/// XEP-0115: Entity Capabilities
void QXmppPresence::setCapabilityVer(const QByteArray& ver)
{
    d->capabilityVer = ver;
}

/// Legacy XEP-0115: Entity Capabilities
QStringList QXmppPresence::capabilityExt() const
{
    return d->capabilityExt;
}

/// Returns the MUC item.

QXmppMucItem QXmppPresence::mucItem() const
{
    return d->mucItem;
}

/// Sets the MUC item.
///
/// \param item

void QXmppPresence::setMucItem(const QXmppMucItem &item)
{
    d->mucItem = item;
}

/// Returns the MUC status codes.

QList<int> QXmppPresence::mucStatusCodes() const
{
    return d->mucStatusCodes;
}

/// Sets the MUC status codes.
///
/// \param codes

void QXmppPresence::setMucStatusCodes(const QList<int> &codes)
{
    d->mucStatusCodes = codes;
}

/// \cond
const QXmppPresence::Status& QXmppPresence::status() const
{
    return d->status;
}

QXmppPresence::Status& QXmppPresence::status()
{
    return d->status;
}

void QXmppPresence::setStatus(const QXmppPresence::Status& status)
{
    d->status = status;
}

QXmppPresence::Status::Status(QXmppPresence::Status::Type type,
                             const QString statusText, int priority) :
                                m_type(type),
                                m_statusText(statusText), m_priority(priority)
{
}

QXmppPresence::Status::Type QXmppPresence::Status::type() const
{
    return m_type;
}

void QXmppPresence::Status::setType(QXmppPresence::Status::Type type)
{
    m_type = type;
}

QString QXmppPresence::Status::statusText() const
{
    return m_statusText;
}

void QXmppPresence::Status::setStatusText(const QString& str)
{
    m_statusText = str;
}

int QXmppPresence::Status::priority() const
{
    return m_priority;
}

void QXmppPresence::Status::setPriority(int priority)
{
    m_priority = priority;
}

void QXmppPresence::Status::parse(const QDomElement &element)
{
    const QString show = element.firstChildElement("show").text();
    for (int i = Online; i <= Invisible; i++) {
        if (show == presence_shows[i]) {
            m_type = static_cast<Type>(i);
            break;
        }
    }
    m_statusText = element.firstChildElement("status").text();
    m_priority = element.firstChildElement("priority").text().toInt();
}

void QXmppPresence::Status::toXml(QXmlStreamWriter *xmlWriter) const
{
    const QString show = presence_shows[m_type];
    if (!show.isEmpty())
        helperToXmlAddTextElement(xmlWriter, "show", show);
    if (!m_statusText.isEmpty())
        helperToXmlAddTextElement(xmlWriter, "status", m_statusText);
    if (m_priority != 0)
        helperToXmlAddTextElement(xmlWriter, "priority", QString::number(m_priority));
}
/// \endcond
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, The University of Oxford
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the University of Oxford nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "apps/lib/oskar_settings_load_ionosphere.h"
#include "sky/oskar_load_TID_parameter_file.h"

#include <QtCore/QString>
#include <QtCore/QByteArray>
#include <QtCore/QSettings>
#include <QtCore/QStringList>

extern "C"
int oskar_settings_load_ionosphere(oskar_SettingsIonosphere* settings,
        const char* filename)
{
    QString temp;
    QByteArray t;
    QStringList list;
    int status = OSKAR_SUCCESS;

    QSettings s(QString(filename), QSettings::IniFormat);
    s.beginGroup("ionosphere");
    settings->enable = (int)s.value("enable", true).toBool();
    settings->min_elevation = (double)s.value("min_elevation_deg", 0.0).toDouble();
    settings->TEC0 = (double)s.value("TEC0", 1.0).toDouble();
    list = s.value("TID_file").toStringList();
    settings->num_TID_screens = list.size();
    settings->TID_files = (char**)malloc(settings->num_TID_screens*sizeof(char*));
    settings->TID = (oskar_SettingsTIDscreen*)malloc(
            settings->num_TID_screens*sizeof(oskar_SettingsTIDscreen));
    for (int i = 0; i < settings->num_TID_screens; ++i)
    {
        t = list[i].toAscii();
        settings->TID_files[i] = (char*)malloc(t.size() + 1);
        strcpy(settings->TID_files[i], t.constData());
        oskar_load_TID_parameter_file(&settings->TID[i], settings->TID_files[i],
                &status);
    }
    s.endGroup(); // ionosphere

    return status;
}
<commit_msg>added missing header<commit_after>/*
 * Copyright (c) 2013, The University of Oxford
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the University of Oxford nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "apps/lib/oskar_settings_load_ionosphere.h"
#include "sky/oskar_load_TID_parameter_file.h"

#include <cstring>
#include <cstdlib>

#include <QtCore/QString>
#include <QtCore/QByteArray>
#include <QtCore/QSettings>
#include <QtCore/QStringList>

extern "C"
int oskar_settings_load_ionosphere(oskar_SettingsIonosphere* settings,
        const char* filename)
{
    QString temp;
    QByteArray t;
    QStringList list;
    int status = OSKAR_SUCCESS;

    QSettings s(QString(filename), QSettings::IniFormat);
    s.beginGroup("ionosphere");
    settings->enable = (int)s.value("enable", true).toBool();
    settings->min_elevation = (double)s.value("min_elevation_deg", 0.0).toDouble();
    settings->TEC0 = (double)s.value("TEC0", 1.0).toDouble();
    list = s.value("TID_file").toStringList();
    settings->num_TID_screens = list.size();
    settings->TID_files = (char**)malloc(settings->num_TID_screens*sizeof(char*));
    settings->TID = (oskar_SettingsTIDscreen*)malloc(
            settings->num_TID_screens*sizeof(oskar_SettingsTIDscreen));
    for (int i = 0; i < settings->num_TID_screens; ++i)
    {
        t = list[i].toAscii();
        settings->TID_files[i] = (char*)malloc(t.size() + 1);
        strcpy(settings->TID_files[i], t.constData());
        oskar_load_TID_parameter_file(&settings->TID[i], settings->TID_files[i],
                &status);
    }
    s.endGroup(); // ionosphere

    return status;
}
<|endoftext|>


<commit_before>/* Copyright (c) 2017, CNRS-LAAS
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#ifndef PLANNING_CPP_INSERTIONS_H
#define PLANNING_CPP_INSERTIONS_H

#include "moves.hpp"

namespace SAOP {

    /** Neighborhood for generating Insert moves.
     *
     * The neighborhood randomly picks a pending point in the visibility structures and returns
     * the best insertion for this point.
     */
    struct OneInsertNbhd final : public Neighborhood {

        std::string name() const override {
            return "one-insert";
        }

        const double default_segment_length = 0.;

        const double default_height = 0.;

        const double max_trials;
        const bool select_arbitrary_trajectory;
        const bool select_arbitrary_position;

        explicit OneInsertNbhd(double max_trials,
                               const bool select_arbitrary_trajectory,
                               const bool select_arbitrary_position)
                : max_trials(max_trials),
                  select_arbitrary_trajectory(select_arbitrary_trajectory),
                  select_arbitrary_position(select_arbitrary_position) {}

        unique_ptr<LocalMove> get_move(PlanPtr p) override {
            UpdateBasedMove no_move(p, unique_ptr<EmptyUpdate>(new EmptyUpdate()));
            unique_ptr<LocalMove> best = {};

            size_t num_tries = 0;
            while (num_tries++ < max_trials) {
                unique_ptr<LocalMove> candidate_move = get_move_for_random_possible_observation(p);
                if (candidate_move) {
                    // a move was generated

                    if (!candidate_move->is_valid())
                        // move is not valid, discard it
                        continue;

                    if (!best && is_better_than(*candidate_move, no_move)) {
                        // no best move, and better than doing nothing
                        best = std::move(candidate_move);
                    } else if (best && is_better_than(*candidate_move, *best)) {
                        // better than the best move
                        best = std::move(candidate_move);
                    }
                }
            }
            return best;
        }

    private:
        /** this move is better than another if it has a significantly better cost or if it has a similar cost but a strictly better duration */
        bool is_better_than(LocalMove& first, LocalMove& other) {
            return localmove_efficiency(first) > localmove_efficiency(other) && first.utility() < other.utility();
        }

        double localmove_efficiency(LocalMove& localmove) {
            double delta_duration = localmove.duration() - localmove.base_plan->duration();
            double delta_utility = -(localmove.utility() - localmove.base_plan->utility());

            if (delta_duration > 0 && delta_utility > 0) {
                return delta_utility / delta_duration;
            } else {
                return 0.;
            }
        }

        /** Picks an observation randomly and generates a move that inserts it into the best looking location. */
        unique_ptr<LocalMove> get_move_for_random_possible_observation(PlanPtr p) {
            ASSERT(!p->trajectories().empty());
            if (p->possible_observations.empty())
                return {};

            /** Select a random point in the pending list */
            const size_t index = rand(0, p->possible_observations.size());
            const PointTimeWindow pt = p->possible_observations[index];

            /** Pick an angle randomly */
            const double random_angle = drand(0, 2 * M_PI);

            /** Waypoint and segment resulting from the random picks */
            const Segment3d random_observation = p->trajectories().uav(0).observation_segment(
                    pt.pt.x, pt.pt.y,
                    default_height + (*p->firedata().elevation)(p->firedata().elevation->as_cell(pt.pt)),
                    random_angle, default_segment_length);

            opt<Candidate> best;

            // last valid projection made of the random observation.
            // it used to incrementally update the projection to different times,
            // Simple measurements indicate that doing this incrementally reduces
            // the number of steps in "project on_firefront" by a factor ~5
            Segment3d projected_random_observation = random_observation;

            size_t first_traj, last_traj;
            if (select_arbitrary_trajectory) {
                const size_t t = rand(0, p->trajectories().size());
                first_traj = t;
                last_traj = t;
            } else {
                first_traj = 0;
                last_traj = p->trajectories().size() - 1;
            }

            /** Try best insert for each subtrajectory in the plan */
            for (size_t i = first_traj; i <= last_traj; i++) {
                const Trajectory& traj = p->trajectories()[i];

                // get projected observation closer to the fire front at the beginning of the trajectory
                // this is useful to avoid to projection to follow the same path multiple times to end up on a failure.
                projected_random_observation = p->firedata().project_closest_to_fire_front(random_observation,
                                                                                traj.conf().uav,
                                                                                traj.start_time());

                size_t first_insertion_loc, last_insertion_loc;
                if (select_arbitrary_position) {
                    const size_t loc = *traj.random_insertion_id();
                    first_insertion_loc = loc;
                    last_insertion_loc = loc;
                } else {
                    first_insertion_loc = traj.insertion_range_start();
                    last_insertion_loc = traj.insertion_range_end();
                }

                for (size_t insert_loc = first_insertion_loc; insert_loc <= last_insertion_loc; insert_loc++) {

                    opt<Segment3d> current_segment = get_projection(p, projected_random_observation, i, insert_loc);

                    if (current_segment) {
                        // save to be able to use it as a base for the following projections
                        projected_random_observation = *current_segment;
                        // current segment is valid (i.e. successfully projected on fire front,
                        // create a candidate for it

                        const double additional_flight_time = traj.insertion_duration_cost(insert_loc,
                                                                                *current_segment);

                        // discard candidate that would go over the max flight time.
                        if (traj.duration() + additional_flight_time > traj.conf().max_flight_time)
                            continue;

                        if (!best || additional_flight_time < best->additional_flight_time) {
                            best = Candidate{i, insert_loc, *current_segment, additional_flight_time};
                        }
                    }
                }
            }

            /** Return the best, if any */
            if (best) {
//                return unique_ptr<UpdateBasedMove>(new UpdateBasedMove(p, unique_ptr<InsertSegmentUpdate>(
//                        new InsertSegmentUpdate(best->traj_id, best->segment, best->insert_loc))));
                return unique_ptr<Insert>(new Insert(p, best->traj_id, best->segment, best->insert_loc));
            } else {
                return {};
            };
        }

    private:
        struct Candidate {
            size_t traj_id;
            size_t insert_loc;
            Segment3d segment;
            double additional_flight_time;
        };

        double
        default_insertion_angle(const Trajectory& traj, size_t insertion_loc, const Segment3d& segment) const {
            if (traj.size() == 0) {
                return segment.start.dir;
            } else if (insertion_loc == 0) {
                // align on next segment
                auto dx = traj[insertion_loc].maneuver.start.x - segment.end.x;
                auto dy = traj[insertion_loc].maneuver.start.y - segment.end.y;
                return atan2(dy, dx);
            } else if (insertion_loc == traj.size()) {
                // align on previous segment
                auto dx = segment.start.x - traj[insertion_loc - 1].maneuver.end.x;
                auto dy = segment.start.y - traj[insertion_loc - 1].maneuver.end.y;
                return atan2(dy, dx);
            } else {
                // take direction from prev to next segment
                auto dx = traj[insertion_loc].maneuver.start.x - traj[insertion_loc - 1].maneuver.end.x;
                auto dy = traj[insertion_loc].maneuver.start.y - traj[insertion_loc - 1].maneuver.end.y;
                return atan2(dy, dx);
            }
        }

        /** Produces a new segment for observation. The segment is such that:
         * if inserted after the insert_loc^th observation of the traj_id^th trajectory, the UAV will reach it when
         * the underneath cell is on fire.
         * If there is no such cell, an empty option is returned. */
        opt<Segment3d>
        get_projection(const PlanPtr& p, const Segment3d to_project, size_t traj_id, size_t insert_loc) {
            const Trajectory& traj = p->trajectories()[traj_id];
            // start iteration from last valid projection made.
            opt<Segment3d> current_segment = to_project;
            bool updated = true;
            size_t num_iter = 0;

            // project the segment on firefront until we converge or are unable to project the segment anymore.
            while (current_segment && updated) {
                // time at which the uav will reach the segment
                const double time = insert_loc == 0 ?
                                    traj.start_time() :
                                    traj.end_time(insert_loc - 1) +
                                    traj.conf().uav.travel_time(traj[insert_loc - 1].maneuver.end,
                                                                to_project.start);
                // back up current segment
                const Segment3d previous = *current_segment;

                // project the segment on the firefront.
                current_segment = p->firedata().project_on_firefront(previous, traj.conf().uav, time);
                ASSERT(!current_segment ||
                       previous.start.dir ==
                       current_segment->start.dir); // projection should not change orientation

                if (current_segment && previous != *current_segment) {
                    // segment has changed due to projection,
                    // set the default orientation to the segment
                    const double direction = default_insertion_angle(traj, insert_loc, *current_segment);
                    current_segment = traj.conf().uav.rotate_on_visibility_center(*current_segment, direction);

                    // start over since the time at which we reach the segment might have changed as well
                    updated = true;
                } else {
                    updated = false;
                }
                ASSERT(num_iter++ <
                       1000); // This should always converges (very quickly). One can safely put a bound on the number of iterations.
            }
            return current_segment;
        }
    };
}

#endif //PLANNING_CPP_INSERTIONS_H
<commit_msg>Consider an Insertion move an improvement if it has strictly better utility<commit_after>/* Copyright (c) 2017, CNRS-LAAS
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#ifndef PLANNING_CPP_INSERTIONS_H
#define PLANNING_CPP_INSERTIONS_H

#include "moves.hpp"

namespace SAOP {

    /** Neighborhood for generating Insert moves.
     *
     * The neighborhood randomly picks a pending point in the visibility structures and returns
     * the best insertion for this point.
     */
    struct OneInsertNbhd final : public Neighborhood {

        std::string name() const override {
            return "one-insert";
        }

        const double default_segment_length = 0.;

        const double default_height = 0.;

        const double max_trials;
        const bool select_arbitrary_trajectory;
        const bool select_arbitrary_position;

        explicit OneInsertNbhd(double max_trials,
                               const bool select_arbitrary_trajectory,
                               const bool select_arbitrary_position)
                : max_trials(max_trials),
                  select_arbitrary_trajectory(select_arbitrary_trajectory),
                  select_arbitrary_position(select_arbitrary_position) {}

        unique_ptr<LocalMove> get_move(PlanPtr p) override {
            UpdateBasedMove no_move(p, unique_ptr<EmptyUpdate>(new EmptyUpdate()));
            unique_ptr<LocalMove> best = {};

            size_t num_tries = 0;
            while (num_tries++ < max_trials) {
                unique_ptr<LocalMove> candidate_move = get_move_for_random_possible_observation(p);
                if (candidate_move) {
                    // a move was generated

                    if (!candidate_move->is_valid())
                        // move is not valid, discard it
                        continue;

                    if (!best && is_better_than(*candidate_move, no_move)) {
                        // no best move, and better than doing nothing
                        best = std::move(candidate_move);
                    } else if (best && is_better_than(*candidate_move, *best)) {
                        // better than the best move
                        best = std::move(candidate_move);
                    }
                }
            }
            return best;
        }

    private:
        /** this move is better than another if it has a significantly better cost or if it has a similar cost but a strictly better duration */
        bool is_better_than(LocalMove& first, LocalMove& other) {
            return localmove_efficiency(first) > localmove_efficiency(other) && (first.utility() + 1) < other.utility();
        }

        double localmove_efficiency(LocalMove& localmove) {
            double delta_duration = localmove.duration() - localmove.base_plan->duration();
            double delta_utility = -(localmove.utility() - localmove.base_plan->utility());

            if (delta_duration > 0 && delta_utility > 0) {
                return delta_utility / delta_duration;
            } else {
                return 0.;
            }
        }

        /** Picks an observation randomly and generates a move that inserts it into the best looking location. */
        unique_ptr<LocalMove> get_move_for_random_possible_observation(PlanPtr p) {
            ASSERT(!p->trajectories().empty());
            if (p->possible_observations.empty())
                return {};

            /** Select a random point in the pending list */
            const size_t index = rand(0, p->possible_observations.size());
            const PointTimeWindow pt = p->possible_observations[index];

            /** Pick an angle randomly */
            const double random_angle = drand(0, 2 * M_PI);

            /** Waypoint and segment resulting from the random picks */
            const Segment3d random_observation = p->trajectories().uav(0).observation_segment(
                    pt.pt.x, pt.pt.y,
                    default_height + (*p->firedata().elevation)(p->firedata().elevation->as_cell(pt.pt)),
                    random_angle, default_segment_length);

            opt<Candidate> best;

            // last valid projection made of the random observation.
            // it used to incrementally update the projection to different times,
            // Simple measurements indicate that doing this incrementally reduces
            // the number of steps in "project on_firefront" by a factor ~5
            Segment3d projected_random_observation = random_observation;

            size_t first_traj, last_traj;
            if (select_arbitrary_trajectory) {
                const size_t t = rand(0, p->trajectories().size());
                first_traj = t;
                last_traj = t;
            } else {
                first_traj = 0;
                last_traj = p->trajectories().size() - 1;
            }

            /** Try best insert for each subtrajectory in the plan */
            for (size_t i = first_traj; i <= last_traj; i++) {
                const Trajectory& traj = p->trajectories()[i];

                // get projected observation closer to the fire front at the beginning of the trajectory
                // this is useful to avoid to projection to follow the same path multiple times to end up on a failure.
                projected_random_observation = p->firedata().project_closest_to_fire_front(random_observation,
                                                                                traj.conf().uav,
                                                                                traj.start_time());

                size_t first_insertion_loc, last_insertion_loc;
                if (select_arbitrary_position) {
                    const size_t loc = *traj.random_insertion_id();
                    first_insertion_loc = loc;
                    last_insertion_loc = loc;
                } else {
                    first_insertion_loc = traj.insertion_range_start();
                    last_insertion_loc = traj.insertion_range_end();
                }

                for (size_t insert_loc = first_insertion_loc; insert_loc <= last_insertion_loc; insert_loc++) {

                    opt<Segment3d> current_segment = get_projection(p, projected_random_observation, i, insert_loc);

                    if (current_segment) {
                        // save to be able to use it as a base for the following projections
                        projected_random_observation = *current_segment;
                        // current segment is valid (i.e. successfully projected on fire front,
                        // create a candidate for it

                        const double additional_flight_time = traj.insertion_duration_cost(insert_loc,
                                                                                *current_segment);

                        // discard candidate that would go over the max flight time.
                        if (traj.duration() + additional_flight_time > traj.conf().max_flight_time)
                            continue;

                        if (!best || additional_flight_time < best->additional_flight_time) {
                            best = Candidate{i, insert_loc, *current_segment, additional_flight_time};
                        }
                    }
                }
            }

            /** Return the best, if any */
            if (best) {
//                return unique_ptr<UpdateBasedMove>(new UpdateBasedMove(p, unique_ptr<InsertSegmentUpdate>(
//                        new InsertSegmentUpdate(best->traj_id, best->segment, best->insert_loc))));
                return unique_ptr<Insert>(new Insert(p, best->traj_id, best->segment, best->insert_loc));
            } else {
                return {};
            };
        }

    private:
        struct Candidate {
            size_t traj_id;
            size_t insert_loc;
            Segment3d segment;
            double additional_flight_time;
        };

        double
        default_insertion_angle(const Trajectory& traj, size_t insertion_loc, const Segment3d& segment) const {
            if (traj.size() == 0) {
                return segment.start.dir;
            } else if (insertion_loc == 0) {
                // align on next segment
                auto dx = traj[insertion_loc].maneuver.start.x - segment.end.x;
                auto dy = traj[insertion_loc].maneuver.start.y - segment.end.y;
                return atan2(dy, dx);
            } else if (insertion_loc == traj.size()) {
                // align on previous segment
                auto dx = segment.start.x - traj[insertion_loc - 1].maneuver.end.x;
                auto dy = segment.start.y - traj[insertion_loc - 1].maneuver.end.y;
                return atan2(dy, dx);
            } else {
                // take direction from prev to next segment
                auto dx = traj[insertion_loc].maneuver.start.x - traj[insertion_loc - 1].maneuver.end.x;
                auto dy = traj[insertion_loc].maneuver.start.y - traj[insertion_loc - 1].maneuver.end.y;
                return atan2(dy, dx);
            }
        }

        /** Produces a new segment for observation. The segment is such that:
         * if inserted after the insert_loc^th observation of the traj_id^th trajectory, the UAV will reach it when
         * the underneath cell is on fire.
         * If there is no such cell, an empty option is returned. */
        opt<Segment3d>
        get_projection(const PlanPtr& p, const Segment3d to_project, size_t traj_id, size_t insert_loc) {
            const Trajectory& traj = p->trajectories()[traj_id];
            // start iteration from last valid projection made.
            opt<Segment3d> current_segment = to_project;
            bool updated = true;
            size_t num_iter = 0;

            // project the segment on firefront until we converge or are unable to project the segment anymore.
            while (current_segment && updated) {
                // time at which the uav will reach the segment
                const double time = insert_loc == 0 ?
                                    traj.start_time() :
                                    traj.end_time(insert_loc - 1) +
                                    traj.conf().uav.travel_time(traj[insert_loc - 1].maneuver.end,
                                                                to_project.start);
                // back up current segment
                const Segment3d previous = *current_segment;

                // project the segment on the firefront.
                current_segment = p->firedata().project_on_firefront(previous, traj.conf().uav, time);
                ASSERT(!current_segment ||
                       previous.start.dir ==
                       current_segment->start.dir); // projection should not change orientation

                if (current_segment && previous != *current_segment) {
                    // segment has changed due to projection,
                    // set the default orientation to the segment
                    const double direction = default_insertion_angle(traj, insert_loc, *current_segment);
                    current_segment = traj.conf().uav.rotate_on_visibility_center(*current_segment, direction);

                    // start over since the time at which we reach the segment might have changed as well
                    updated = true;
                } else {
                    updated = false;
                }
                ASSERT(num_iter++ <
                       1000); // This should always converges (very quickly). One can safely put a bound on the number of iterations.
            }
            return current_segment;
        }
    };
}

#endif //PLANNING_CPP_INSERTIONS_H
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Jules Cléro
 *
 * 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.
 *
 */

#include "libDroneMovement/Drone.hpp"

#include <cstdint>
#include <chrono>
#include <thread>
#include <exception>

namespace ghost
{
namespace libDroneMovement
{

Drone::Drone(const std::string& droneIpAddress)
    : mAtCommandFactory(), mAtCommandSender(droneIpAddress, mAtCommandFactory),
    mNavDataParser(), mNavDataReceiver(droneIpAddress, mNavDataParser)
{

    /**
     * TODO: Synchronise NavData Thread
     */

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    if (mNavDataReceiver.isSetUp()) {
        mAtCommandSender.start();

        // Initialize horizontal plane
        mAtCommandSender.addCommandToSend(mAtCommandFactory.horizontalPlaneCalibrationCommand());
        std::this_thread::sleep_for(std::chrono::seconds(3));

        if (mNavDataParser.isControlCommandAck()) {

            mAtCommandSender.addCommandToSend(mAtCommandFactory.ackConfigureCommand());

            mNavDataParser.waitNotControlCommandAck();
        }

        // Set Drone Max altitude
        setConfiguration("control:altitude_max", "2000");

        // Initialize NavData Sending
        setConfiguration("general:navdata_demo", "TRUE");
    } else {
        throw std::runtime_error("NavData service unavailable");
    }
}

Drone::~Drone()
{
    mNavDataReceiver.stop();
    mNavDataReceiver.wait();
    mAtCommandSender.stop();
    mAtCommandSender.wait();
}

void Drone::takeOff()
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.takeOffCommand());

    /**
     * Wait that the drone take off.
     * We can't use NavData to detect if the take off has ended because
     * the fly bit state is set as soon as the drone receive the take-off
     * AT command.
     */
    std::this_thread::sleep_for(std::chrono::seconds(4));
}

void Drone::land()
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.landCommand());

    /**
     * Wait that the drone land.
     * We can't use NavData to detect if the land has ended because
     * the fly bit state is unset as soon as the drone receive the land
     * AT command.
     */
    std::this_thread::sleep_for(std::chrono::seconds(4));
}

void Drone::hover()
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.hoveringCommand());
}

void Drone::movement(float roll, float pitch, float gaz, float yaw)
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.movementCommand(roll, pitch, gaz, yaw));
}

void Drone::movement(float roll, float pitch, float gaz)
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.movementCommand(roll, pitch, gaz));
}

void Drone::setConfiguration(const std::string& argA, const std::string& argB)
{
    // Send command
    mAtCommandSender.addCommandToSend(mAtCommandFactory.configureCommand(argA, argB));

    // Wait Drone's ACK
    mNavDataParser.waitControlCommandAck();

    // Reset Drone's ACK Bit
    mAtCommandSender.addCommandToSend(mAtCommandFactory.ackConfigureCommand());

    // Wait Reset
    mNavDataParser.waitNotControlCommandAck();
}

void Drone::setEmergencyMode()
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.emergencyCommand());
}

int Drone::getAltitude()
{
    return mNavDataParser.getCurrentNavData().altitude;
}

float Drone::getPhiAngle()
{
    return mNavDataParser.getCurrentNavData().phi;
}

float Drone::getThetaAngle()
{
    return mNavDataParser.getCurrentNavData().theta;
}

float Drone::getPsiAngle()
{
    return mNavDataParser.getCurrentNavData().psi;
}

} /* libDroneMovement namespace  */
} /* ghost namespace */
<commit_msg>[libDroneMovement] Change Drone angle getter unit to degrees<commit_after>/*
 * Copyright 2014 Jules Cléro
 *
 * 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.
 *
 */

#include "libDroneMovement/Drone.hpp"

#include <cstdint>
#include <chrono>
#include <thread>
#include <exception>

namespace ghost
{
namespace libDroneMovement
{

Drone::Drone(const std::string& droneIpAddress)
    : mAtCommandFactory(), mAtCommandSender(droneIpAddress, mAtCommandFactory),
    mNavDataParser(), mNavDataReceiver(droneIpAddress, mNavDataParser)
{

    /**
     * TODO: Synchronise NavData Thread
     */

    std::this_thread::sleep_for(std::chrono::milliseconds(500));

    if (mNavDataReceiver.isSetUp()) {
        mAtCommandSender.start();

        // Initialize horizontal plane
        mAtCommandSender.addCommandToSend(mAtCommandFactory.horizontalPlaneCalibrationCommand());
        std::this_thread::sleep_for(std::chrono::seconds(3));

        if (mNavDataParser.isControlCommandAck()) {

            mAtCommandSender.addCommandToSend(mAtCommandFactory.ackConfigureCommand());

            mNavDataParser.waitNotControlCommandAck();
        }

        // Set Drone Max altitude
        setConfiguration("control:altitude_max", "2000");

        // Initialize NavData Sending
        setConfiguration("general:navdata_demo", "TRUE");
    } else {
        throw std::runtime_error("NavData service unavailable");
    }
}

Drone::~Drone()
{
    mNavDataReceiver.stop();
    mNavDataReceiver.wait();
    mAtCommandSender.stop();
    mAtCommandSender.wait();
}

void Drone::takeOff()
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.takeOffCommand());

    /**
     * Wait that the drone take off.
     * We can't use NavData to detect if the take off has ended because
     * the fly bit state is set as soon as the drone receive the take-off
     * AT command.
     */
    std::this_thread::sleep_for(std::chrono::seconds(4));
}

void Drone::land()
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.landCommand());

    /**
     * Wait that the drone land.
     * We can't use NavData to detect if the land has ended because
     * the fly bit state is unset as soon as the drone receive the land
     * AT command.
     */
    std::this_thread::sleep_for(std::chrono::seconds(4));
}

void Drone::hover()
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.hoveringCommand());
}

void Drone::movement(float roll, float pitch, float gaz, float yaw)
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.movementCommand(roll, pitch, gaz, yaw));
}

void Drone::movement(float roll, float pitch, float gaz)
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.movementCommand(roll, pitch, gaz));
}

void Drone::setConfiguration(const std::string& argA, const std::string& argB)
{
    // Send command
    mAtCommandSender.addCommandToSend(mAtCommandFactory.configureCommand(argA, argB));

    // Wait Drone's ACK
    mNavDataParser.waitControlCommandAck();

    // Reset Drone's ACK Bit
    mAtCommandSender.addCommandToSend(mAtCommandFactory.ackConfigureCommand());

    // Wait Reset
    mNavDataParser.waitNotControlCommandAck();
}

void Drone::setEmergencyMode()
{
    mAtCommandSender.addCommandToSend(mAtCommandFactory.emergencyCommand());
}

int Drone::getAltitude()
{
    return mNavDataParser.getCurrentNavData().altitude;
}

float Drone::getPhiAngle()
{
    return mNavDataParser.getCurrentNavData().phi / 1000.0;
}

float Drone::getThetaAngle()
{
    return mNavDataParser.getCurrentNavData().theta / 1000.0;
}

float Drone::getPsiAngle()
{
    return mNavDataParser.getCurrentNavData().psi / 1000.0;
}

} /* libDroneMovement namespace  */
} /* ghost namespace */
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.

#include "libcef/browser/menu_creator.h"
#include "libcef/browser/browser_host_impl.h"
#include "libcef/browser/context_menu_params_impl.h"
#include "libcef/common/content_client.h"

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "grit/cef_strings.h"

#if defined(OS_WIN)
#include "libcef/browser/menu_creator_runner_win.h"
#elif defined(OS_MACOSX)
#include "libcef/browser/menu_creator_runner_mac.h"
#elif defined(TOOLKIT_GTK)
#include "libcef/browser/menu_creator_runner_gtk.h"
#endif

namespace {

CefString GetLabel(int message_id) {
  string16 label = CefContentClient::Get()->GetLocalizedString(message_id);
  DCHECK(!label.empty());
  return label;
}

}  // namespace

CefMenuCreator::CefMenuCreator(CefBrowserHostImpl* browser)
  : browser_(browser) {
  model_ = new CefMenuModelImpl(this);
}

CefMenuCreator::~CefMenuCreator() {
  // The model may outlive the delegate if the context menu is visible when the
  // application is closed.
  model_->set_delegate(NULL);
}

bool CefMenuCreator::IsShowingContextMenu() {
  content::WebContents* web_contents = browser_->GetWebContents();
  if (!web_contents)
    return false;
  content::RenderWidgetHostView* view = web_contents->GetRenderWidgetHostView();
  return (view && view->IsShowingContextMenu());
}

bool CefMenuCreator::CreateContextMenu(
    const content::ContextMenuParams& params) {
  if (!CreateRunner())
    return true;

  // The renderer may send the "show context menu" message multiple times, one
  // for each right click mouse event it receives. Normally, this doesn't happen
  // because mouse events are not forwarded once the context menu is showing.
  // However, there's a race - the context menu may not yet be showing when
  // the second mouse event arrives. In this case, |HandleContextMenu()| will
  // get called multiple times - if so, don't create another context menu.
  // TODO(asvitkine): Fix the renderer so that it doesn't do this.
  if (IsShowingContextMenu())
    return true;

  params_ = params;
  model_->Clear();

  // Create the default menu model.
  CreateDefaultModel();

  // Give the client a chance to modify the model.
  CefRefPtr<CefClient> client = browser_->GetClient();
  if (client.get()) {
      CefRefPtr<CefContextMenuHandler> handler =
          client->GetContextMenuHandler();
      if (handler.get()) {
        CefRefPtr<CefFrame> frame;
        if (params_.frame_id > 0)
          frame = browser_->GetFrame(params_.frame_id);
        if (!frame.get())
          frame = browser_->GetMainFrame();

        CefRefPtr<CefContextMenuParamsImpl> paramsPtr(
            new CefContextMenuParamsImpl(&params_));

        handler->OnBeforeContextMenu(browser_, frame, paramsPtr.get(),
                                     model_.get());

        // Do not keep references to the parameters in the callback.
        paramsPtr->Detach(NULL);
        DCHECK_EQ(paramsPtr->GetRefCt(), 1);
        DCHECK(model_->VerifyRefCount());

        // Menu is empty so notify the client and return.
        if (model_->GetCount() == 0) {
          MenuClosed(model_);
          return true;
        }
      }
  }

  return runner_->RunContextMenu(this);
}

bool CefMenuCreator::CreateRunner() {
  if (!runner_.get()) {
    // Create the menu runner.
#if defined(OS_WIN)
    runner_.reset(new CefMenuCreatorRunnerWin);
#elif defined(OS_MACOSX)
    runner_.reset(new CefMenuCreatorRunnerMac);
#elif defined(TOOLKIT_GTK)
    runner_.reset(new CefMenuCreatorRunnerGtk);
#else
    // Need an implementation.
    NOTREACHED();
#endif
  }
  return (runner_.get() != NULL);
}

void CefMenuCreator::ExecuteCommand(CefRefPtr<CefMenuModelImpl> source,
                                    int command_id,
                                    cef_event_flags_t event_flags) {
  // Give the client a chance to handle the command.
  CefRefPtr<CefClient> client = browser_->GetClient();
  if (client.get()) {
      CefRefPtr<CefContextMenuHandler> handler =
          client->GetContextMenuHandler();
      if (handler.get()) {
        CefRefPtr<CefFrame> frame;
        if (params_.frame_id > 0)
          frame = browser_->GetFrame(params_.frame_id);
        if (!frame.get())
          frame = browser_->GetMainFrame();

        CefRefPtr<CefContextMenuParamsImpl> paramsPtr(
            new CefContextMenuParamsImpl(&params_));

        bool handled = handler->OnContextMenuCommand(browser_, frame,
            paramsPtr.get(), command_id, event_flags);

        // Do not keep references to the parameters in the callback.
        paramsPtr->Detach(NULL);
        DCHECK_EQ(paramsPtr->GetRefCt(), 1);

        if (handled)
          return;
      }
  }

  // Execute the default command handling.
  ExecuteDefaultCommand(command_id);
}

void CefMenuCreator::MenuWillShow(CefRefPtr<CefMenuModelImpl> source) {
  // May be called for sub-menus as well.
  if (source.get() != model_.get())
    return;

  content::WebContents* web_contents = browser_->GetWebContents();
  if (!web_contents)
    return;

  // Notify the host before showing the context menu.
  content::RenderWidgetHostView* view = web_contents->GetRenderWidgetHostView();
  if (view)
    view->SetShowingContextMenu(true);
}

void CefMenuCreator::MenuClosed(CefRefPtr<CefMenuModelImpl> source) {
  // May be called for sub-menus as well.
  if (source.get() != model_.get())
    return;

  // Notify the client.
  CefRefPtr<CefClient> client = browser_->GetClient();
  if (client.get()) {
      CefRefPtr<CefContextMenuHandler> handler =
          client->GetContextMenuHandler();
      if (handler.get()) {
        CefRefPtr<CefFrame> frame;
        if (params_.frame_id > 0)
          frame = browser_->GetFrame(params_.frame_id);
        if (!frame.get())
          frame = browser_->GetMainFrame();

        handler->OnContextMenuDismissed(browser_, frame);
      }
  }

  if (IsShowingContextMenu()) {
    // Notify the host after closing the context menu.
    content::RenderWidgetHostView* view =
        browser_->GetWebContents()->GetRenderWidgetHostView();
    if (view)
      view->SetShowingContextMenu(false);
    content::RenderViewHost* rvh =
        browser_->GetWebContents()->GetRenderViewHost();
    if (rvh)
      rvh->NotifyContextMenuClosed(params_.custom_context);
  }
}

void CefMenuCreator::CreateDefaultModel() {
  if (params_.is_editable) {
    // Editable node.
    model_->AddItem(MENU_ID_UNDO, GetLabel(IDS_MENU_UNDO));
    model_->AddItem(MENU_ID_REDO, GetLabel(IDS_MENU_REDO));

    model_->AddSeparator();
    model_->AddItem(MENU_ID_CUT, GetLabel(IDS_MENU_CUT));
    model_->AddItem(MENU_ID_COPY, GetLabel(IDS_MENU_COPY));
    model_->AddItem(MENU_ID_PASTE, GetLabel(IDS_MENU_PASTE));
    model_->AddItem(MENU_ID_DELETE, GetLabel(IDS_MENU_DELETE));

    model_->AddSeparator();
    model_->AddItem(MENU_ID_SELECT_ALL, GetLabel(IDS_MENU_SELECT_ALL));

    if (!(params_.edit_flags & CM_EDITFLAG_CAN_UNDO))
      model_->SetEnabled(MENU_ID_UNDO, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_REDO))
      model_->SetEnabled(MENU_ID_REDO, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_CUT))
      model_->SetEnabled(MENU_ID_CUT, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_COPY))
      model_->SetEnabled(MENU_ID_COPY, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_PASTE))
      model_->SetEnabled(MENU_ID_PASTE, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_DELETE))
      model_->SetEnabled(MENU_ID_DELETE, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_SELECT_ALL))
      model_->SetEnabled(MENU_ID_SELECT_ALL, false);
  } else if (!params_.selection_text.empty()) {
    // Something is selected.
    model_->AddItem(MENU_ID_COPY, GetLabel(IDS_MENU_COPY));
  } else if (!params_.page_url.is_empty() || !params_.frame_url.is_empty()) {
    // Page or frame.
    model_->AddItem(MENU_ID_BACK, GetLabel(IDS_MENU_BACK));
    model_->AddItem(MENU_ID_FORWARD, GetLabel(IDS_MENU_FORWARD));

    model_->AddSeparator();
    model_->AddItem(MENU_ID_PRINT, GetLabel(IDS_MENU_PRINT));
    model_->AddItem(MENU_ID_VIEW_SOURCE, GetLabel(IDS_MENU_VIEW_SOURCE));

    if (!browser_->CanGoBack())
      model_->SetEnabled(MENU_ID_BACK, false);
    if (!browser_->CanGoForward())
      model_->SetEnabled(MENU_ID_FORWARD, false);

    // TODO(cef): Enable once printing is supported.
    model_->SetEnabled(MENU_ID_PRINT, false);
  }
}

void CefMenuCreator::ExecuteDefaultCommand(int command_id) {
  switch (command_id) {
  // Navigation.
  case MENU_ID_BACK:
    browser_->GoBack();
    break;
  case MENU_ID_FORWARD:
    browser_->GoForward();
    break;
  case MENU_ID_RELOAD:
    browser_->Reload();
    break;
  case MENU_ID_RELOAD_NOCACHE:
    browser_->ReloadIgnoreCache();
    break;
  case MENU_ID_STOPLOAD:
    browser_->StopLoad();
    break;

  // Editing.
  case MENU_ID_UNDO:
    browser_->GetFocusedFrame()->Undo();
    break;
  case MENU_ID_REDO:
    browser_->GetFocusedFrame()->Redo();
    break;
  case MENU_ID_CUT:
    browser_->GetFocusedFrame()->Cut();
    break;
  case MENU_ID_COPY:
    browser_->GetFocusedFrame()->Copy();
    break;
  case MENU_ID_PASTE:
    browser_->GetFocusedFrame()->Paste();
    break;
  case MENU_ID_DELETE:
    browser_->GetFocusedFrame()->Delete();
    break;
  case MENU_ID_SELECT_ALL:
    browser_->GetFocusedFrame()->SelectAll();
    break;

  // Miscellaneous.
  case MENU_ID_FIND:
    // TODO(cef): Implement.
    NOTIMPLEMENTED();
    break;
  case MENU_ID_PRINT:
    // TODO(cef): Implement.
    NOTIMPLEMENTED();
    break;
  case MENU_ID_VIEW_SOURCE:
    browser_->GetFocusedFrame()->ViewSource();
    break;

  default:
    break;
  }
}
<commit_msg>Enable print option in context menu (issue #1127).<commit_after>// Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.

#include "libcef/browser/menu_creator.h"
#include "libcef/browser/browser_host_impl.h"
#include "libcef/browser/context_menu_params_impl.h"
#include "libcef/common/content_client.h"

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "grit/cef_strings.h"

#if defined(OS_WIN)
#include "libcef/browser/menu_creator_runner_win.h"
#elif defined(OS_MACOSX)
#include "libcef/browser/menu_creator_runner_mac.h"
#elif defined(TOOLKIT_GTK)
#include "libcef/browser/menu_creator_runner_gtk.h"
#endif

namespace {

CefString GetLabel(int message_id) {
  string16 label = CefContentClient::Get()->GetLocalizedString(message_id);
  DCHECK(!label.empty());
  return label;
}

}  // namespace

CefMenuCreator::CefMenuCreator(CefBrowserHostImpl* browser)
  : browser_(browser) {
  model_ = new CefMenuModelImpl(this);
}

CefMenuCreator::~CefMenuCreator() {
  // The model may outlive the delegate if the context menu is visible when the
  // application is closed.
  model_->set_delegate(NULL);
}

bool CefMenuCreator::IsShowingContextMenu() {
  content::WebContents* web_contents = browser_->GetWebContents();
  if (!web_contents)
    return false;
  content::RenderWidgetHostView* view = web_contents->GetRenderWidgetHostView();
  return (view && view->IsShowingContextMenu());
}

bool CefMenuCreator::CreateContextMenu(
    const content::ContextMenuParams& params) {
  if (!CreateRunner())
    return true;

  // The renderer may send the "show context menu" message multiple times, one
  // for each right click mouse event it receives. Normally, this doesn't happen
  // because mouse events are not forwarded once the context menu is showing.
  // However, there's a race - the context menu may not yet be showing when
  // the second mouse event arrives. In this case, |HandleContextMenu()| will
  // get called multiple times - if so, don't create another context menu.
  // TODO(asvitkine): Fix the renderer so that it doesn't do this.
  if (IsShowingContextMenu())
    return true;

  params_ = params;
  model_->Clear();

  // Create the default menu model.
  CreateDefaultModel();

  // Give the client a chance to modify the model.
  CefRefPtr<CefClient> client = browser_->GetClient();
  if (client.get()) {
      CefRefPtr<CefContextMenuHandler> handler =
          client->GetContextMenuHandler();
      if (handler.get()) {
        CefRefPtr<CefFrame> frame;
        if (params_.frame_id > 0)
          frame = browser_->GetFrame(params_.frame_id);
        if (!frame.get())
          frame = browser_->GetMainFrame();

        CefRefPtr<CefContextMenuParamsImpl> paramsPtr(
            new CefContextMenuParamsImpl(&params_));

        handler->OnBeforeContextMenu(browser_, frame, paramsPtr.get(),
                                     model_.get());

        // Do not keep references to the parameters in the callback.
        paramsPtr->Detach(NULL);
        DCHECK_EQ(paramsPtr->GetRefCt(), 1);
        DCHECK(model_->VerifyRefCount());

        // Menu is empty so notify the client and return.
        if (model_->GetCount() == 0) {
          MenuClosed(model_);
          return true;
        }
      }
  }

  return runner_->RunContextMenu(this);
}

bool CefMenuCreator::CreateRunner() {
  if (!runner_.get()) {
    // Create the menu runner.
#if defined(OS_WIN)
    runner_.reset(new CefMenuCreatorRunnerWin);
#elif defined(OS_MACOSX)
    runner_.reset(new CefMenuCreatorRunnerMac);
#elif defined(TOOLKIT_GTK)
    runner_.reset(new CefMenuCreatorRunnerGtk);
#else
    // Need an implementation.
    NOTREACHED();
#endif
  }
  return (runner_.get() != NULL);
}

void CefMenuCreator::ExecuteCommand(CefRefPtr<CefMenuModelImpl> source,
                                    int command_id,
                                    cef_event_flags_t event_flags) {
  // Give the client a chance to handle the command.
  CefRefPtr<CefClient> client = browser_->GetClient();
  if (client.get()) {
      CefRefPtr<CefContextMenuHandler> handler =
          client->GetContextMenuHandler();
      if (handler.get()) {
        CefRefPtr<CefFrame> frame;
        if (params_.frame_id > 0)
          frame = browser_->GetFrame(params_.frame_id);
        if (!frame.get())
          frame = browser_->GetMainFrame();

        CefRefPtr<CefContextMenuParamsImpl> paramsPtr(
            new CefContextMenuParamsImpl(&params_));

        bool handled = handler->OnContextMenuCommand(browser_, frame,
            paramsPtr.get(), command_id, event_flags);

        // Do not keep references to the parameters in the callback.
        paramsPtr->Detach(NULL);
        DCHECK_EQ(paramsPtr->GetRefCt(), 1);

        if (handled)
          return;
      }
  }

  // Execute the default command handling.
  ExecuteDefaultCommand(command_id);
}

void CefMenuCreator::MenuWillShow(CefRefPtr<CefMenuModelImpl> source) {
  // May be called for sub-menus as well.
  if (source.get() != model_.get())
    return;

  content::WebContents* web_contents = browser_->GetWebContents();
  if (!web_contents)
    return;

  // Notify the host before showing the context menu.
  content::RenderWidgetHostView* view = web_contents->GetRenderWidgetHostView();
  if (view)
    view->SetShowingContextMenu(true);
}

void CefMenuCreator::MenuClosed(CefRefPtr<CefMenuModelImpl> source) {
  // May be called for sub-menus as well.
  if (source.get() != model_.get())
    return;

  // Notify the client.
  CefRefPtr<CefClient> client = browser_->GetClient();
  if (client.get()) {
      CefRefPtr<CefContextMenuHandler> handler =
          client->GetContextMenuHandler();
      if (handler.get()) {
        CefRefPtr<CefFrame> frame;
        if (params_.frame_id > 0)
          frame = browser_->GetFrame(params_.frame_id);
        if (!frame.get())
          frame = browser_->GetMainFrame();

        handler->OnContextMenuDismissed(browser_, frame);
      }
  }

  if (IsShowingContextMenu()) {
    // Notify the host after closing the context menu.
    content::RenderWidgetHostView* view =
        browser_->GetWebContents()->GetRenderWidgetHostView();
    if (view)
      view->SetShowingContextMenu(false);
    content::RenderViewHost* rvh =
        browser_->GetWebContents()->GetRenderViewHost();
    if (rvh)
      rvh->NotifyContextMenuClosed(params_.custom_context);
  }
}

void CefMenuCreator::CreateDefaultModel() {
  if (params_.is_editable) {
    // Editable node.
    model_->AddItem(MENU_ID_UNDO, GetLabel(IDS_MENU_UNDO));
    model_->AddItem(MENU_ID_REDO, GetLabel(IDS_MENU_REDO));

    model_->AddSeparator();
    model_->AddItem(MENU_ID_CUT, GetLabel(IDS_MENU_CUT));
    model_->AddItem(MENU_ID_COPY, GetLabel(IDS_MENU_COPY));
    model_->AddItem(MENU_ID_PASTE, GetLabel(IDS_MENU_PASTE));
    model_->AddItem(MENU_ID_DELETE, GetLabel(IDS_MENU_DELETE));

    model_->AddSeparator();
    model_->AddItem(MENU_ID_SELECT_ALL, GetLabel(IDS_MENU_SELECT_ALL));

    if (!(params_.edit_flags & CM_EDITFLAG_CAN_UNDO))
      model_->SetEnabled(MENU_ID_UNDO, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_REDO))
      model_->SetEnabled(MENU_ID_REDO, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_CUT))
      model_->SetEnabled(MENU_ID_CUT, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_COPY))
      model_->SetEnabled(MENU_ID_COPY, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_PASTE))
      model_->SetEnabled(MENU_ID_PASTE, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_DELETE))
      model_->SetEnabled(MENU_ID_DELETE, false);
    if (!(params_.edit_flags & CM_EDITFLAG_CAN_SELECT_ALL))
      model_->SetEnabled(MENU_ID_SELECT_ALL, false);
  } else if (!params_.selection_text.empty()) {
    // Something is selected.
    model_->AddItem(MENU_ID_COPY, GetLabel(IDS_MENU_COPY));
  } else if (!params_.page_url.is_empty() || !params_.frame_url.is_empty()) {
    // Page or frame.
    model_->AddItem(MENU_ID_BACK, GetLabel(IDS_MENU_BACK));
    model_->AddItem(MENU_ID_FORWARD, GetLabel(IDS_MENU_FORWARD));

    model_->AddSeparator();
    model_->AddItem(MENU_ID_PRINT, GetLabel(IDS_MENU_PRINT));
    model_->AddItem(MENU_ID_VIEW_SOURCE, GetLabel(IDS_MENU_VIEW_SOURCE));

    if (!browser_->CanGoBack())
      model_->SetEnabled(MENU_ID_BACK, false);
    if (!browser_->CanGoForward())
      model_->SetEnabled(MENU_ID_FORWARD, false);
  }
}

void CefMenuCreator::ExecuteDefaultCommand(int command_id) {
  switch (command_id) {
  // Navigation.
  case MENU_ID_BACK:
    browser_->GoBack();
    break;
  case MENU_ID_FORWARD:
    browser_->GoForward();
    break;
  case MENU_ID_RELOAD:
    browser_->Reload();
    break;
  case MENU_ID_RELOAD_NOCACHE:
    browser_->ReloadIgnoreCache();
    break;
  case MENU_ID_STOPLOAD:
    browser_->StopLoad();
    break;

  // Editing.
  case MENU_ID_UNDO:
    browser_->GetFocusedFrame()->Undo();
    break;
  case MENU_ID_REDO:
    browser_->GetFocusedFrame()->Redo();
    break;
  case MENU_ID_CUT:
    browser_->GetFocusedFrame()->Cut();
    break;
  case MENU_ID_COPY:
    browser_->GetFocusedFrame()->Copy();
    break;
  case MENU_ID_PASTE:
    browser_->GetFocusedFrame()->Paste();
    break;
  case MENU_ID_DELETE:
    browser_->GetFocusedFrame()->Delete();
    break;
  case MENU_ID_SELECT_ALL:
    browser_->GetFocusedFrame()->SelectAll();
    break;

  // Miscellaneous.
  case MENU_ID_FIND:
    // TODO(cef): Implement.
    NOTIMPLEMENTED();
    break;
  case MENU_ID_PRINT:
    browser_->Print();
    break;
  case MENU_ID_VIEW_SOURCE:
    browser_->GetFocusedFrame()->ViewSource();
    break;

  default:
    break;
  }
}
<|endoftext|>