<commit_before>/*
  Copyright (C) 2008-2015 The Communi Project

  You may use this file under the terms of BSD license as follows:

  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.
    * Neither the name of the copyright holder 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 HOLDERS 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 "zncmanager.h"
#include "ignoremanager.h"
#include <ircbuffermodel.h>
#include <ircconnection.h>
#include <irccommand.h>
#include <ircmessage.h>
#include <ircbuffer.h>

IRC_USE_NAMESPACE

ZncManager::ZncManager(QObject* parent) : QObject(parent)
{
    d.model = 0;
    d.timestamp = QDateTime::fromTime_t(0);
    setModel(qobject_cast<IrcBufferModel*>(parent));
}

ZncManager::~ZncManager()
{
}

IrcBufferModel* ZncManager::model() const
{
    return d.model;
}

void ZncManager::setModel(IrcBufferModel* model)
{
    if (d.model != model) {
        if (d.model && d.model->connection()) {
            IrcConnection* connection = d.model->connection();
            disconnect(connection, SIGNAL(connected()), this, SLOT(requestPlayback()));
            connection->removeMessageFilter(this);
            disconnect(model, SIGNAL(removed(IrcBuffer*)), this, SLOT(clearBuffer(IrcBuffer*)));
        }
        d.model = model;
        if (d.model && d.model->connection()) {
            IrcNetwork* network = d.model->network();
            QStringList caps = network->requestedCapabilities();
            caps += "batch";
            caps += "server-time";
            caps += "echo-message";
            caps += "znc.in/batch";
            caps += "znc.in/playback";
            caps += "znc.in/server-time";
            caps += "znc.in/echo-message";
            caps += "znc.in/server-time-iso";
            network->setRequestedCapabilities(caps);

            IrcConnection* connection = d.model->connection();
            connect(connection, SIGNAL(connected()), this, SLOT(requestPlayback()));
            connection->installMessageFilter(this);
            connect(model, SIGNAL(removed(IrcBuffer*)), this, SLOT(clearBuffer(IrcBuffer*)));
        }
        emit modelChanged(model);
    }
}

bool ZncManager::messageFilter(IrcMessage* message)
{
    if (message->connection()->isConnected())
        d.timestamp = qMax(d.timestamp, message->timeStamp());

    if (message->type() == IrcMessage::Batch) {
        IrcBatchMessage* batch = static_cast<IrcBatchMessage*>(message);
        if (batch->batch() == "znc.in/playback") {
            IrcBuffer* buffer = d.model->add(batch->parameters().value(2));
            foreach (IrcMessage* msg, batch->messages()) {
                msg->setFlags(msg->flags() | IrcMessage::Playback);
                if (msg->type() == IrcMessage::Private)
                    processMessage(static_cast<IrcPrivateMessage*>(msg));
            }
            buffer->receiveMessage(batch);
        }
    }

    return IgnoreManager::instance()->messageFilter(message);
}

void ZncManager::processMessage(IrcPrivateMessage* message)
{
    if (message->nick() == "*buffextras") {
        const QString msg = message->content();
        const int idx = msg.indexOf(" ");
        const QString prefix = msg.left(idx);
        const QString content = msg.mid(idx + 1);

        message->setPrefix(prefix);
        if (content.startsWith("joined")) {
            message->setTag("intent", "JOIN");
            message->setParameters(QStringList() << message->target());
        } else if (content.startsWith("parted")) {
            message->setTag("intent", "PART");
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            message->setParameters(QStringList() << message->target() << reason);
        } else if (content.startsWith("quit")) {
            message->setTag("intent", "QUIT");
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            message->setParameters(QStringList() << reason);
        } else if (content.startsWith("is")) {
            message->setTag("intent", "NICK");
            const QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            message->setParameters(QStringList() << tokens.last());
        } else if (content.startsWith("set")) {
            message->setTag("intent", "MODE");
            QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            const QString user = tokens.takeLast();
            const QString mode = tokens.takeLast();
            message->setParameters(QStringList() << message->target() << mode << user);
        } else if (content.startsWith("changed")) {
            message->setTag("intent", "TOPIC");
            const QString topic = content.mid(content.indexOf(":") + 2);
            message->setParameters(QStringList() << message->target() << topic);
        } else if (content.startsWith("kicked")) {
            message->setTag("intent", "KICK");
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            message->setParameters(QStringList() << message->target() << tokens.value(1) << reason);
        }
    }
}

void ZncManager::requestPlayback()
{
    if (d.model->network()->isCapable("znc.in/playback")) {
        IrcConnection* connection = d.model->connection();
        IrcCommand* cmd = IrcCommand::createMessage("*playback", QString("PLAY * %1").arg(d.timestamp.isValid() ? d.timestamp.toTime_t() : 0));
        cmd->setParent(this);
        connection->sendCommand(cmd);
    }
}

void ZncManager::clearBuffer(IrcBuffer* buffer)
{
    if (d.model->network()->isCapable("znc.in/playback") && !buffer->title().contains("*"))
        buffer->sendCommand(IrcCommand::createMessage("*playback", QString("CLEAR %1").arg(buffer->title())));
}
<commit_msg>Revert "ZncManager: don't filter out batch messages"<commit_after>/*
  Copyright (C) 2008-2015 The Communi Project

  You may use this file under the terms of BSD license as follows:

  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.
    * Neither the name of the copyright holder 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 HOLDERS 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 "zncmanager.h"
#include "ignoremanager.h"
#include <ircbuffermodel.h>
#include <ircconnection.h>
#include <irccommand.h>
#include <ircmessage.h>
#include <ircbuffer.h>

IRC_USE_NAMESPACE

ZncManager::ZncManager(QObject* parent) : QObject(parent)
{
    d.model = 0;
    d.timestamp = QDateTime::fromTime_t(0);
    setModel(qobject_cast<IrcBufferModel*>(parent));
}

ZncManager::~ZncManager()
{
}

IrcBufferModel* ZncManager::model() const
{
    return d.model;
}

void ZncManager::setModel(IrcBufferModel* model)
{
    if (d.model != model) {
        if (d.model && d.model->connection()) {
            IrcConnection* connection = d.model->connection();
            disconnect(connection, SIGNAL(connected()), this, SLOT(requestPlayback()));
            connection->removeMessageFilter(this);
            disconnect(model, SIGNAL(removed(IrcBuffer*)), this, SLOT(clearBuffer(IrcBuffer*)));
        }
        d.model = model;
        if (d.model && d.model->connection()) {
            IrcNetwork* network = d.model->network();
            QStringList caps = network->requestedCapabilities();
            caps += "batch";
            caps += "server-time";
            caps += "echo-message";
            caps += "znc.in/batch";
            caps += "znc.in/playback";
            caps += "znc.in/server-time";
            caps += "znc.in/echo-message";
            caps += "znc.in/server-time-iso";
            network->setRequestedCapabilities(caps);

            IrcConnection* connection = d.model->connection();
            connect(connection, SIGNAL(connected()), this, SLOT(requestPlayback()));
            connection->installMessageFilter(this);
            connect(model, SIGNAL(removed(IrcBuffer*)), this, SLOT(clearBuffer(IrcBuffer*)));
        }
        emit modelChanged(model);
    }
}

bool ZncManager::messageFilter(IrcMessage* message)
{
    if (message->connection()->isConnected())
        d.timestamp = qMax(d.timestamp, message->timeStamp());

    if (message->type() == IrcMessage::Batch) {
        IrcBatchMessage* batch = static_cast<IrcBatchMessage*>(message);
        if (batch->batch() == "znc.in/playback") {
            IrcBuffer* buffer = d.model->add(batch->parameters().value(2));
            foreach (IrcMessage* msg, batch->messages()) {
                msg->setFlags(msg->flags() | IrcMessage::Playback);
                if (msg->type() == IrcMessage::Private)
                    processMessage(static_cast<IrcPrivateMessage*>(msg));
            }
            buffer->receiveMessage(batch);
            return true;
        }
    }

    return IgnoreManager::instance()->messageFilter(message);
}

void ZncManager::processMessage(IrcPrivateMessage* message)
{
    if (message->nick() == "*buffextras") {
        const QString msg = message->content();
        const int idx = msg.indexOf(" ");
        const QString prefix = msg.left(idx);
        const QString content = msg.mid(idx + 1);

        message->setPrefix(prefix);
        if (content.startsWith("joined")) {
            message->setTag("intent", "JOIN");
            message->setParameters(QStringList() << message->target());
        } else if (content.startsWith("parted")) {
            message->setTag("intent", "PART");
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            message->setParameters(QStringList() << message->target() << reason);
        } else if (content.startsWith("quit")) {
            message->setTag("intent", "QUIT");
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            message->setParameters(QStringList() << reason);
        } else if (content.startsWith("is")) {
            message->setTag("intent", "NICK");
            const QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            message->setParameters(QStringList() << tokens.last());
        } else if (content.startsWith("set")) {
            message->setTag("intent", "MODE");
            QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            const QString user = tokens.takeLast();
            const QString mode = tokens.takeLast();
            message->setParameters(QStringList() << message->target() << mode << user);
        } else if (content.startsWith("changed")) {
            message->setTag("intent", "TOPIC");
            const QString topic = content.mid(content.indexOf(":") + 2);
            message->setParameters(QStringList() << message->target() << topic);
        } else if (content.startsWith("kicked")) {
            message->setTag("intent", "KICK");
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            message->setParameters(QStringList() << message->target() << tokens.value(1) << reason);
        }
    }
}

void ZncManager::requestPlayback()
{
    if (d.model->network()->isCapable("znc.in/playback")) {
        IrcConnection* connection = d.model->connection();
        IrcCommand* cmd = IrcCommand::createMessage("*playback", QString("PLAY * %1").arg(d.timestamp.isValid() ? d.timestamp.toTime_t() : 0));
        cmd->setParent(this);
        connection->sendCommand(cmd);
    }
}

void ZncManager::clearBuffer(IrcBuffer* buffer)
{
    if (d.model->network()->isCapable("znc.in/playback") && !buffer->title().contains("*"))
        buffer->sendCommand(IrcCommand::createMessage("*playback", QString("CLEAR %1").arg(buffer->title())));
}
<|endoftext|>


<commit_before>/*
  Copyright (C) 2008-2014 The Communi Project

  You may use this file under the terms of BSD license as follows:

  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.
    * Neither the name of the copyright holder 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 HOLDERS 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 "zncmanager.h"
#include "ignoremanager.h"
#include <ircbuffermodel.h>
#include <ircconnection.h>
#include <irccommand.h>
#include <ircmessage.h>
#include <ircbuffer.h>

IRC_USE_NAMESPACE

ZncManager::ZncManager(QObject* parent) : QObject(parent)
{
    d.model = 0;
    d.buffer = 0;
    d.timestamp = QDateTime::fromTime_t(0);
    setModel(qobject_cast<IrcBufferModel*>(parent));
}

ZncManager::~ZncManager()
{
}

IrcBufferModel* ZncManager::model() const
{
    return d.model;
}

void ZncManager::setModel(IrcBufferModel* model)
{
    if (d.model != model) {
        if (d.model && d.model->connection()) {
            IrcConnection* connection = d.model->connection();
            disconnect(connection, SIGNAL(connected()), this, SLOT(requestPlayback()));
            disconnect(connection->network(), SIGNAL(requestingCapabilities()), this, SLOT(requestCapabilities()));
            connection->removeMessageFilter(this);
            disconnect(model, SIGNAL(removed(IrcBuffer*)), this, SLOT(clearBuffer(IrcBuffer*)));
        }
        d.model = model;
        if (d.model && d.model->connection()) {
            IrcConnection* connection = d.model->connection();
            connect(connection, SIGNAL(connected()), this, SLOT(requestPlayback()));
            connect(connection->network(), SIGNAL(requestingCapabilities()), this, SLOT(requestCapabilities()));
            connection->installMessageFilter(this);
            connect(model, SIGNAL(removed(IrcBuffer*)), this, SLOT(clearBuffer(IrcBuffer*)));
        }
        emit modelChanged(model);
    }
}

bool ZncManager::messageFilter(IrcMessage* message)
{
    bool playback = false;
    if (message->tags().contains("time")) {
        QDateTime timestamp = message->tags().value("time").toDateTime();
        if (timestamp.isValid()) {
            message->setTimeStamp(timestamp.toTimeSpec(Qt::LocalTime));
            playback = timestamp < d.timestamp;
            d.timestamp = qMax(timestamp, d.timestamp);
        }
    }

    if (!playback && d.buffer) {
        emit playbackEnd(d.buffer);
        d.buffer = 0;
    }

    if (message->type() == IrcMessage::Private) {
        IrcPrivateMessage* msg = static_cast<IrcPrivateMessage*>(message);
        IrcBuffer* buffer = d.model->find(msg->target());
        if (buffer) {
            if (d.buffer != buffer) {
                if (d.buffer) {
                    emit playbackEnd(d.buffer);
                    d.buffer = 0;
                }
                if (playback) {
                    emit playbackBegin(buffer);
                    d.buffer = buffer;
                }
            }
            return processMessage(buffer, msg);
        }
    }
    return IgnoreManager::instance()->messageFilter(message);
}

bool ZncManager::processMessage(IrcBuffer* buffer, IrcPrivateMessage* message)
{
    if (message->nick() == "*buffextras") {
        const QString msg = message->content();
        const int idx = msg.indexOf(" ");
        const QString prefix = msg.left(idx);
        const QString content = msg.mid(idx + 1);

        IrcMessage* tmp = 0;
        if (content.startsWith("joined")) {
            tmp = IrcMessage::fromParameters(prefix, "JOIN", QStringList() << message->target(), message->connection());
        } else if (content.startsWith("parted")) {
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            tmp = IrcMessage::fromParameters(prefix, "PART", QStringList() << message->target() << reason , message->connection());
        } else if (content.startsWith("quit")) {
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            tmp = IrcMessage::fromParameters(prefix, "QUIT", QStringList() << reason , message->connection());
        } else if (content.startsWith("is")) {
            const QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            tmp = IrcMessage::fromParameters(prefix, "NICK", QStringList() << tokens.last() , message->connection());
        } else if (content.startsWith("set")) {
            QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            const QString user = tokens.takeLast();
            const QString mode = tokens.takeLast();
            tmp = IrcMessage::fromParameters(prefix, "MODE", QStringList() << message->target() << mode << user, message->connection());
        } else if (content.startsWith("changed")) {
            const QString topic = content.mid(content.indexOf(":") + 2);
            tmp = IrcMessage::fromParameters(prefix, "TOPIC", QStringList() << message->target() << topic, message->connection());
        } else if (content.startsWith("kicked")) {
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            tmp = IrcMessage::fromParameters(prefix, "KICK", QStringList() << message->target() << tokens.value(1) << reason, message->connection());
        }
        if (tmp) {
            tmp->setTags(message->tags());
            tmp->setTimeStamp(message->timeStamp());
            buffer->receiveMessage(tmp);
            tmp->deleteLater();
            return true;
        }
    }
    return IgnoreManager::instance()->messageFilter(message);
}

void ZncManager::requestPlayback()
{
    if (d.model->network()->isCapable("znc.in/playback")) {
        IrcConnection* connection = d.model->connection();
        connection->sendCommand(IrcCommand::createMessage("*playback", QString("PLAY * %1").arg(d.timestamp.isValid() ? d.timestamp.toTime_t() : 0)));
    }
}

void ZncManager::requestCapabilities()
{
    QStringList request;
    QStringList available = d.model->network()->availableCapabilities();

    if (available.contains("znc.in/playback"))
        request << "znc.in/playback";

    if (available.contains("server-time"))
        request << "server-time";
    else if (available.contains("znc.in/server-time-iso"))
        request << "znc.in/server-time-iso";
    else if (available.contains("znc.in/server-time"))
        request << "znc.in/server-time";

    if (!request.isEmpty())
        d.model->network()->requestCapabilities(request);
}

void ZncManager::clearBuffer(IrcBuffer* buffer)
{
    if (d.model->network()->isCapable("znc.in/playback") && !buffer->title().contains("*"))
        buffer->sendCommand(IrcCommand::createMessage("*playback", QString("CLEAR %1").arg(buffer->title())));
}
<commit_msg>ZNC: request self-message capability<commit_after>/*
  Copyright (C) 2008-2014 The Communi Project

  You may use this file under the terms of BSD license as follows:

  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.
    * Neither the name of the copyright holder 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 HOLDERS 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 "zncmanager.h"
#include "ignoremanager.h"
#include <ircbuffermodel.h>
#include <ircconnection.h>
#include <irccommand.h>
#include <ircmessage.h>
#include <ircbuffer.h>

IRC_USE_NAMESPACE

ZncManager::ZncManager(QObject* parent) : QObject(parent)
{
    d.model = 0;
    d.buffer = 0;
    d.timestamp = QDateTime::fromTime_t(0);
    setModel(qobject_cast<IrcBufferModel*>(parent));
}

ZncManager::~ZncManager()
{
}

IrcBufferModel* ZncManager::model() const
{
    return d.model;
}

void ZncManager::setModel(IrcBufferModel* model)
{
    if (d.model != model) {
        if (d.model && d.model->connection()) {
            IrcConnection* connection = d.model->connection();
            disconnect(connection, SIGNAL(connected()), this, SLOT(requestPlayback()));
            disconnect(connection->network(), SIGNAL(requestingCapabilities()), this, SLOT(requestCapabilities()));
            connection->removeMessageFilter(this);
            disconnect(model, SIGNAL(removed(IrcBuffer*)), this, SLOT(clearBuffer(IrcBuffer*)));
        }
        d.model = model;
        if (d.model && d.model->connection()) {
            IrcConnection* connection = d.model->connection();
            connect(connection, SIGNAL(connected()), this, SLOT(requestPlayback()));
            connect(connection->network(), SIGNAL(requestingCapabilities()), this, SLOT(requestCapabilities()));
            connection->installMessageFilter(this);
            connect(model, SIGNAL(removed(IrcBuffer*)), this, SLOT(clearBuffer(IrcBuffer*)));
        }
        emit modelChanged(model);
    }
}

bool ZncManager::messageFilter(IrcMessage* message)
{
    bool playback = false;
    if (message->tags().contains("time")) {
        QDateTime timestamp = message->tags().value("time").toDateTime();
        if (timestamp.isValid()) {
            message->setTimeStamp(timestamp.toTimeSpec(Qt::LocalTime));
            playback = timestamp < d.timestamp;
            d.timestamp = qMax(timestamp, d.timestamp);
        }
    }

    if (!playback && d.buffer) {
        emit playbackEnd(d.buffer);
        d.buffer = 0;
    }

    if (message->type() == IrcMessage::Private) {
        IrcPrivateMessage* msg = static_cast<IrcPrivateMessage*>(message);
        IrcBuffer* buffer = d.model->find(msg->target());
        if (buffer) {
            if (d.buffer != buffer) {
                if (d.buffer) {
                    emit playbackEnd(d.buffer);
                    d.buffer = 0;
                }
                if (playback) {
                    emit playbackBegin(buffer);
                    d.buffer = buffer;
                }
            }
            return processMessage(buffer, msg);
        }
    }
    return IgnoreManager::instance()->messageFilter(message);
}

bool ZncManager::processMessage(IrcBuffer* buffer, IrcPrivateMessage* message)
{
    if (message->nick() == "*buffextras") {
        const QString msg = message->content();
        const int idx = msg.indexOf(" ");
        const QString prefix = msg.left(idx);
        const QString content = msg.mid(idx + 1);

        IrcMessage* tmp = 0;
        if (content.startsWith("joined")) {
            tmp = IrcMessage::fromParameters(prefix, "JOIN", QStringList() << message->target(), message->connection());
        } else if (content.startsWith("parted")) {
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            tmp = IrcMessage::fromParameters(prefix, "PART", QStringList() << message->target() << reason , message->connection());
        } else if (content.startsWith("quit")) {
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            tmp = IrcMessage::fromParameters(prefix, "QUIT", QStringList() << reason , message->connection());
        } else if (content.startsWith("is")) {
            const QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            tmp = IrcMessage::fromParameters(prefix, "NICK", QStringList() << tokens.last() , message->connection());
        } else if (content.startsWith("set")) {
            QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            const QString user = tokens.takeLast();
            const QString mode = tokens.takeLast();
            tmp = IrcMessage::fromParameters(prefix, "MODE", QStringList() << message->target() << mode << user, message->connection());
        } else if (content.startsWith("changed")) {
            const QString topic = content.mid(content.indexOf(":") + 2);
            tmp = IrcMessage::fromParameters(prefix, "TOPIC", QStringList() << message->target() << topic, message->connection());
        } else if (content.startsWith("kicked")) {
            QString reason = content.mid(content.indexOf("[") + 1);
            reason.chop(1);
            QStringList tokens = content.split(" ", QString::SkipEmptyParts);
            tmp = IrcMessage::fromParameters(prefix, "KICK", QStringList() << message->target() << tokens.value(1) << reason, message->connection());
        }
        if (tmp) {
            tmp->setTags(message->tags());
            tmp->setTimeStamp(message->timeStamp());
            buffer->receiveMessage(tmp);
            tmp->deleteLater();
            return true;
        }
    }
    return IgnoreManager::instance()->messageFilter(message);
}

void ZncManager::requestPlayback()
{
    if (d.model->network()->isCapable("znc.in/playback")) {
        IrcConnection* connection = d.model->connection();
        connection->sendCommand(IrcCommand::createMessage("*playback", QString("PLAY * %1").arg(d.timestamp.isValid() ? d.timestamp.toTime_t() : 0)));
    }
}

void ZncManager::requestCapabilities()
{
    QStringList request;
    QStringList available = d.model->network()->availableCapabilities();

    if (available.contains("self-message"))
        request << "self-message";

    if (available.contains("znc.in/playback"))
        request << "znc.in/playback";

    if (available.contains("server-time"))
        request << "server-time";
    else if (available.contains("znc.in/server-time-iso"))
        request << "znc.in/server-time-iso";
    else if (available.contains("znc.in/server-time"))
        request << "znc.in/server-time";

    if (!request.isEmpty())
        d.model->network()->requestCapabilities(request);
}

void ZncManager::clearBuffer(IrcBuffer* buffer)
{
    if (d.model->network()->isCapable("znc.in/playback") && !buffer->title().contains("*"))
        buffer->sendCommand(IrcCommand::createMessage("*playback", QString("CLEAR %1").arg(buffer->title())));
}
<|endoftext|>


<commit_before>//===========================================
//  Lumina-DE source code
//  Copyright (c) 2014, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================
#include "AppMenu.h"
#include "LSession.h"
#include <LuminaOS.h>

AppMenu::AppMenu(QWidget* parent) : QMenu(parent){
  appstorelink = LOS::AppStoreShortcut(); //Default application "store" to display (AppCafe in PC-BSD)
  controlpanellink = LOS::ControlPanelShortcut(); //Default control panel
  APPS.clear();
  watcher = new QFileSystemWatcher(this);
    connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherUpdate()) );
  //QTimer::singleShot(200, this, SLOT(start()) ); //Now start filling the menu
  start(); //do the initial run during session init so things are responsive immediately.
  connect(QApplication::instance(), SIGNAL(LocaleChanged()), this, SLOT(watcherUpdate()) );
  connect(QApplication::instance(), SIGNAL(IconThemeChanged()), this, SLOT(watcherUpdate()) );
}

AppMenu::~AppMenu(){

}

QHash<QString, QList<XDGDesktop> >* AppMenu::currentAppHash(){
  return &APPS;
}

//===========
//  PRIVATE
//===========
void AppMenu::updateAppList(){
  //Make sure the title/icon are updated as well (in case of locale/icon change)
  this->setTitle(tr("Applications"));
  this->setIcon( LXDG::findIcon("system-run","") );
  //Now update the lists
  this->clear();
  APPS.clear();
  QList<XDGDesktop> allfiles = LXDG::systemDesktopFiles();
  APPS = LXDG::sortDesktopCats(allfiles);
  APPS.insert("All", LXDG::sortDesktopNames(allfiles));
  lastHashUpdate = QDateTime::currentDateTime();
  //Now fill the menu
  bool ok; //for checking inputs
    //Add link to the file manager
    this->addAction( LXDG::findIcon("user-home", ""), tr("Browse Files"), this, SLOT(launchFileManager()) );
    //--Look for the app store
    XDGDesktop store = LXDG::loadDesktopFile(appstorelink, ok);
    if(ok){
      this->addAction( LXDG::findIcon(store.icon, ""), tr("Install Applications"), this, SLOT(launchStore()) );
    }
    //--Look for the control panel
    store = LXDG::loadDesktopFile(controlpanellink, ok);
    if(ok){
      this->addAction( LXDG::findIcon(store.icon, ""), tr("Control Panel"), this, SLOT(launchControlPanel()) );
    }
    this->addSeparator();
    //--Now create the sub-menus
    QStringList cats = APPS.keys();
    cats.sort(); //make sure they are alphabetical
    for(int i=0; i<cats.length(); i++){
      //Make sure they are translated and have the right icons
      QString name, icon;
      if(cats[i]=="All"){continue; } //skip this listing for the menu
      else if(cats[i] == "Multimedia"){ name = tr("Multimedia"); icon = "applications-multimedia"; }
      else if(cats[i] == "Development"){ name = tr("Development"); icon = "applications-development"; }
      else if(cats[i] == "Education"){ name = tr("Education"); icon = "applications-education"; }
      else if(cats[i] == "Game"){ name = tr("Games"); icon = "applications-games"; }
      else if(cats[i] == "Graphics"){ name = tr("Graphics"); icon = "applications-graphics"; }
      else if(cats[i] == "Network"){ name = tr("Network"); icon = "applications-internet"; }
      else if(cats[i] == "Office"){ name = tr("Office"); icon = "applications-office"; }
      else if(cats[i] == "Science"){ name = tr("Science"); icon = "applications-science"; }
      else if(cats[i] == "Settings"){ name = tr("Settings"); icon = "preferences-system"; }
      else if(cats[i] == "System"){ name = tr("System"); icon = "applications-system"; }
      else if(cats[i] == "Utility"){ name = tr("Utility"); icon = "applications-utilities"; }
      else if(cats[i] == "Wine"){ name = tr("Wine"); icon = "wine"; }
      else{ name = tr("Unsorted"); icon = "applications-other"; }

      QMenu *menu = new QMenu(name, this);
      menu->setIcon(LXDG::findIcon(icon,""));
      connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(launchApp(QAction*)) );
      QList<XDGDesktop> appL = APPS.value(cats[i]);
      for( int a=0; a<appL.length(); a++){
	if(appL[a].actions.isEmpty()){
	  //Just a single entry point - no extra actions
          QAction *act = new QAction(LXDG::findIcon(appL[a].icon, ""), appL[a].name, this);
          act->setToolTip(appL[a].comment);
          act->setWhatsThis(appL[a].filePath);
          menu->addAction(act);
	}else{
	  //This app has additional actions - make this a sub menu
	  // - first the main menu/action
	  QMenu *submenu = new QMenu(appL[a].name, this);
	    submenu->setIcon( LXDG::findIcon(appL[a].icon,"") );
	      //This is the normal behavior - not a special sub-action (although it needs to be at the top of the new menu)
	      QAction *act = new QAction(LXDG::findIcon(appL[a].icon, ""), appL[a].name, this);
              act->setToolTip(appL[a].comment);
              act->setWhatsThis(appL[a].filePath);
	    submenu->addAction(act);
	    //Now add entries for every sub-action listed
	    for(int sa=0; sa<appL[a].actions.length(); sa++){
              QAction *sact = new QAction(LXDG::findIcon(appL[a].actions[sa].icon, appL[a].icon), appL[a].actions[sa].name, this);
              sact->setToolTip(appL[a].comment);
              sact->setWhatsThis("-action \""+appL[a].actions[sa].ID+"\" \""+appL[a].filePath+"\"");
              submenu->addAction(sact);
	    }
	  menu->addMenu(submenu);
	}
      }
      this->addMenu(menu);
    }
    emit AppMenuUpdated();
}

//=================
//  PRIVATE SLOTS
//=================
void AppMenu::start(){
  //Setup the watcher
  watcher->addPaths(LXDG::systemApplicationDirs());
  //Now fill the menu the first time
  updateAppList();
}

void AppMenu::watcherUpdate(){
  updateAppList(); //Update the menu listings
}

void AppMenu::launchStore(){
  LSession::LaunchApplication("lumina-open \""+appstorelink+"\"");
}

void AppMenu::launchControlPanel(){
  LSession::LaunchApplication("lumina-open \""+controlpanellink+"\"");
}

void AppMenu::launchFileManager(){
  QString fm = "lumina-open \""+QDir::homePath()+"\"";
  LSession::LaunchApplication(fm);
}

void AppMenu::launchApp(QAction *act){
  QString appFile = act->whatsThis();
  if(appFile.startsWith("-action")){
    LSession::LaunchApplication("lumina-open "+appFile); //already has quotes put in place properly
  }else{
    LSession::LaunchApplication("lumina-open \""+appFile+"\"");
  }
}
<commit_msg>Remove the "Browse Files" link from the application menu (does not fit well here - use other links instead).<commit_after>//===========================================
//  Lumina-DE source code
//  Copyright (c) 2014, Ken Moore
//  Available under the 3-clause BSD license
//  See the LICENSE file for full details
//===========================================
#include "AppMenu.h"
#include "LSession.h"
#include <LuminaOS.h>

AppMenu::AppMenu(QWidget* parent) : QMenu(parent){
  appstorelink = LOS::AppStoreShortcut(); //Default application "store" to display (AppCafe in PC-BSD)
  controlpanellink = LOS::ControlPanelShortcut(); //Default control panel
  APPS.clear();
  watcher = new QFileSystemWatcher(this);
    connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherUpdate()) );
  //QTimer::singleShot(200, this, SLOT(start()) ); //Now start filling the menu
  start(); //do the initial run during session init so things are responsive immediately.
  connect(QApplication::instance(), SIGNAL(LocaleChanged()), this, SLOT(watcherUpdate()) );
  connect(QApplication::instance(), SIGNAL(IconThemeChanged()), this, SLOT(watcherUpdate()) );
}

AppMenu::~AppMenu(){

}

QHash<QString, QList<XDGDesktop> >* AppMenu::currentAppHash(){
  return &APPS;
}

//===========
//  PRIVATE
//===========
void AppMenu::updateAppList(){
  //Make sure the title/icon are updated as well (in case of locale/icon change)
  this->setTitle(tr("Applications"));
  this->setIcon( LXDG::findIcon("system-run","") );
  //Now update the lists
  this->clear();
  APPS.clear();
  QList<XDGDesktop> allfiles = LXDG::systemDesktopFiles();
  APPS = LXDG::sortDesktopCats(allfiles);
  APPS.insert("All", LXDG::sortDesktopNames(allfiles));
  lastHashUpdate = QDateTime::currentDateTime();
  //Now fill the menu
  bool ok; //for checking inputs
    //Add link to the file manager
    //this->addAction( LXDG::findIcon("user-home", ""), tr("Browse Files"), this, SLOT(launchFileManager()) );
    //--Look for the app store
    XDGDesktop store = LXDG::loadDesktopFile(appstorelink, ok);
    if(ok){
      this->addAction( LXDG::findIcon(store.icon, ""), tr("Install Applications"), this, SLOT(launchStore()) );
    }
    //--Look for the control panel
    store = LXDG::loadDesktopFile(controlpanellink, ok);
    if(ok){
      this->addAction( LXDG::findIcon(store.icon, ""), tr("Control Panel"), this, SLOT(launchControlPanel()) );
    }
    this->addSeparator();
    //--Now create the sub-menus
    QStringList cats = APPS.keys();
    cats.sort(); //make sure they are alphabetical
    for(int i=0; i<cats.length(); i++){
      //Make sure they are translated and have the right icons
      QString name, icon;
      if(cats[i]=="All"){continue; } //skip this listing for the menu
      else if(cats[i] == "Multimedia"){ name = tr("Multimedia"); icon = "applications-multimedia"; }
      else if(cats[i] == "Development"){ name = tr("Development"); icon = "applications-development"; }
      else if(cats[i] == "Education"){ name = tr("Education"); icon = "applications-education"; }
      else if(cats[i] == "Game"){ name = tr("Games"); icon = "applications-games"; }
      else if(cats[i] == "Graphics"){ name = tr("Graphics"); icon = "applications-graphics"; }
      else if(cats[i] == "Network"){ name = tr("Network"); icon = "applications-internet"; }
      else if(cats[i] == "Office"){ name = tr("Office"); icon = "applications-office"; }
      else if(cats[i] == "Science"){ name = tr("Science"); icon = "applications-science"; }
      else if(cats[i] == "Settings"){ name = tr("Settings"); icon = "preferences-system"; }
      else if(cats[i] == "System"){ name = tr("System"); icon = "applications-system"; }
      else if(cats[i] == "Utility"){ name = tr("Utility"); icon = "applications-utilities"; }
      else if(cats[i] == "Wine"){ name = tr("Wine"); icon = "wine"; }
      else{ name = tr("Unsorted"); icon = "applications-other"; }

      QMenu *menu = new QMenu(name, this);
      menu->setIcon(LXDG::findIcon(icon,""));
      connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(launchApp(QAction*)) );
      QList<XDGDesktop> appL = APPS.value(cats[i]);
      for( int a=0; a<appL.length(); a++){
	if(appL[a].actions.isEmpty()){
	  //Just a single entry point - no extra actions
          QAction *act = new QAction(LXDG::findIcon(appL[a].icon, ""), appL[a].name, this);
          act->setToolTip(appL[a].comment);
          act->setWhatsThis(appL[a].filePath);
          menu->addAction(act);
	}else{
	  //This app has additional actions - make this a sub menu
	  // - first the main menu/action
	  QMenu *submenu = new QMenu(appL[a].name, this);
	    submenu->setIcon( LXDG::findIcon(appL[a].icon,"") );
	      //This is the normal behavior - not a special sub-action (although it needs to be at the top of the new menu)
	      QAction *act = new QAction(LXDG::findIcon(appL[a].icon, ""), appL[a].name, this);
              act->setToolTip(appL[a].comment);
              act->setWhatsThis(appL[a].filePath);
	    submenu->addAction(act);
	    //Now add entries for every sub-action listed
	    for(int sa=0; sa<appL[a].actions.length(); sa++){
              QAction *sact = new QAction(LXDG::findIcon(appL[a].actions[sa].icon, appL[a].icon), appL[a].actions[sa].name, this);
              sact->setToolTip(appL[a].comment);
              sact->setWhatsThis("-action \""+appL[a].actions[sa].ID+"\" \""+appL[a].filePath+"\"");
              submenu->addAction(sact);
	    }
	  menu->addMenu(submenu);
	}
      }
      this->addMenu(menu);
    }
    emit AppMenuUpdated();
}

//=================
//  PRIVATE SLOTS
//=================
void AppMenu::start(){
  //Setup the watcher
  watcher->addPaths(LXDG::systemApplicationDirs());
  //Now fill the menu the first time
  updateAppList();
}

void AppMenu::watcherUpdate(){
  updateAppList(); //Update the menu listings
}

void AppMenu::launchStore(){
  LSession::LaunchApplication("lumina-open \""+appstorelink+"\"");
}

void AppMenu::launchControlPanel(){
  LSession::LaunchApplication("lumina-open \""+controlpanellink+"\"");
}

void AppMenu::launchFileManager(){
  QString fm = "lumina-open \""+QDir::homePath()+"\"";
  LSession::LaunchApplication(fm);
}

void AppMenu::launchApp(QAction *act){
  QString appFile = act->whatsThis();
  if(appFile.startsWith("-action")){
    LSession::LaunchApplication("lumina-open "+appFile); //already has quotes put in place properly
  }else{
    LSession::LaunchApplication("lumina-open \""+appFile+"\"");
  }
}
<|endoftext|>


<commit_before>#include "blockstream.h"

#define MASK_5BIT  0x000001F
#define MASK_10BIT 0x00003FF
#define MASK_16BIT 0x000FFFF
#define MASK_26BIT 0x3FFFFFF
#define MASK_28BIT 0xFFFFFFF

namespace redsea {

uint16_t syndrome(int vec) {

  uint16_t synd_reg = 0x000;
  int bit,l;

  for (int k=25; k>=0; k--) {
    bit       = (vec & (1 << k));
    l         = (synd_reg & 0x200);      // Store lefmost bit of register
    synd_reg  = (synd_reg << 1) & 0x3FF; // Rotate register
    synd_reg ^= (bit ? 0x31B : 0x00);    // Premultiply input by x^325 mod g(x)
    synd_reg ^= (l   ? 0x1B9 : 0x00);    // Division mod 2 by g(x)
  }

  return synd_reg;
}

BlockStream::BlockStream() : has_sync_for_(5), group_data_(4), has_block_(5),
  bitcount_(0), left_to_read_(0), bit_stream_(), wideblock_(0), block_has_errors_(50)

{

  offset_word_ = {0x0FC, 0x198, 0x168, 0x350, 0x1B4};

  block_for_offset_ = {0, 1, 2, 2, 3};

  error_lookup_ = {
    {0x200, 0b1000000000000000},
    {0x300, 0b1100000000000000},
    {0x180, 0b0110000000000000},
    {0x0c0, 0b0011000000000000},
    {0x060, 0b0001100000000000},
    {0x030, 0b0000110000000000},
    {0x018, 0b0000011000000000},
    {0x00c, 0b0000001100000000},
    {0x006, 0b0000000110000000},
    {0x003, 0b0000000011000000},
    {0x2dd, 0b0000000001100000},
    {0x3b2, 0b0000000000110000},
    {0x1d9, 0b0000000000011000},
    {0x230, 0b0000000000001100},
    {0x118, 0b0000000000000110},
    {0x08c, 0b0000000000000011},
    {0x046, 0b0000000000000001}
  };
}

void BlockStream::uncorrectable() {
  printf(":offset %d not received\n",expected_offset_);
  int data_length = 0;

  // TODO: return partial group
  /*if (has_block_[A]) {
    data_length = 1;

    if (has_block_[B]) {
      data_length = 2;

      if (has_block_[C] || has_block_[CI]) {
        data_length = 3;
      }
    }
  }*/

  block_has_errors_[block_counter_ % block_has_errors_.size()] = true;

  int erroneous_blocks = 0;
  for (bool e : block_has_errors_) {
    if (e)
      erroneous_blocks ++;
  }

  // Sync is lost when >45 out of last 50 blocks are erroneous (Section C.1.2)
  if (is_in_sync_ && erroneous_blocks > 45) {
    is_in_sync_ = false;
    for (int i=0; i<block_has_errors_.size(); i++)
      block_has_errors_[i] = false;
    pi_ = 0x0000;
    printf(":too many errors, sync lost\n");
  }

  for (int o=A; o<=D; o++)
    has_block_[o] = false;

}

std::vector<uint16_t> BlockStream::getNextGroup() {

  has_whole_group_ = false;

  while (!has_whole_group_ && !isEOF()) {

    // Compensate for clock slip corrections
    bitcount_ += 26 - left_to_read_;

    // Read from radio
    for (int i=0; i < (is_in_sync_ ? left_to_read_ : 1); i++, bitcount_++) {
      wideblock_ = (wideblock_ << 1) + bit_stream_.getNextBit();
    }

    left_to_read_ = 26;
    wideblock_ &= MASK_28BIT;

    int block = (wideblock_ >> 1) & MASK_26BIT;

    // Find the offsets for which the syndrome is zero
    bool has_sync_for_any = false;
    for (int o=A; o<=D; o++) {
      has_sync_for_[o] = (syndrome(block ^ offset_word_[o]) == 0x000);
      has_sync_for_any |= has_sync_for_[o];
    }

    // Acquire sync

    if (!is_in_sync_) {
      if (has_sync_for_any) {
        for (int o=A; o<=D; o++) {
          if (has_sync_for_[o]) {
            int dist = bitcount_ - prevbitcount_;

            if (dist % 26 == 0 && dist <= 156 &&
                (block_for_offset_[prevsync_] + dist/26) % 4 == block_for_offset_[o]) {
              is_in_sync_ = true;
              expected_offset_ = o;
              printf(":sync!\n");
            } else {
              prevbitcount_ = bitcount_;
              prevsync_ = o;
            }
          }
        }
      }
    }

    // Synchronous decoding

    if (is_in_sync_) {

      block_counter_ ++;
      uint16_t message = block >> 10;

      if (expected_offset_ == C && !has_sync_for_[C] && has_sync_for_[CI]) {
        expected_offset_ = CI;
      }

      if ( !has_sync_for_[expected_offset_]) {

        // If message is a correct PI, error was probably in check bits
        if (expected_offset_ == A && message == pi_ && pi_ != 0) {
          has_sync_for_[A] = true;
          printf(":ignoring error in check bits\n");
        } else if (expected_offset_ == C && message == pi_ && pi_ != 0) {
          has_sync_for_[CI] = true;
          printf(":ignoring error in check bits\n");

        // Detect & correct clock slips (Section C.1.2)
        } else if (expected_offset_ == A && pi_ != 0 &&
            ((wideblock_ >> 12) & MASK_16BIT) == pi_) {
          message = pi_;
          wideblock_ >>= 1;
          has_sync_for_[A] = true;
          printf(":clock slip corrected\n");
        } else if (expected_offset_ == A && pi_ != 0 &&
            ((wideblock_ >> 10) & MASK_16BIT) == pi_) {
          message = pi_;
          wideblock_ = (wideblock_ << 1) + bit_stream_.getNextBit();
          has_sync_for_[A] = true;
          left_to_read_ = 25;
          printf(":clock slip corrected\n");

        // Detect & correct burst errors (Section B.2.2)
        } else {

          uint16_t synd_reg = syndrome(block ^ offset_word_[expected_offset_]);

          if (pi_ != 0 && expected_offset_ == A) {
            printf(":expecting PI%04x, got %04x, xor %d, syndrome %03x\n",
                pi_, block>>10, pi_ ^ (block>>10), synd_reg);
          }

          if (error_lookup_.find(synd_reg) != error_lookup_.end()) {
            message = (block >> 10) ^ error_lookup_[synd_reg];
            has_sync_for_[expected_offset_] = true;

            printf(":error corrected block %d using vector %04x for syndrome %03x\n",
                expected_offset_, error_lookup_[synd_reg], synd_reg);

          }

        }

        // Still no sync pulse
        if ( !has_sync_for_[expected_offset_]) {
          uncorrectable();
        }
      }

      // Error-free block received

      if (has_sync_for_[expected_offset_]) {

        group_data_[block_for_offset_[expected_offset_]] = message;
        has_block_[expected_offset_] = true;

        if (expected_offset_ == A) {
          pi_ = message;
        }

        // Complete group received
        if (has_block_[A] && has_block_[B] && (has_block_[C] || has_block_[CI]) && has_block_[D]) {
          has_whole_group_ = true;
        }
      }

      expected_offset_ = (expected_offset_ == C ? D : (expected_offset_ + 1) % 5);

      if (expected_offset_ == A) {
        for (int o=A; o<=D; o++)
          has_block_[o] = false;
      }

    }

  }

  return group_data_;

}

bool BlockStream::isEOF() const {
  return bit_stream_.isEOF();
}

} // namespace redsea
<commit_msg>suppress integer warning<commit_after>#include "blockstream.h"

#define MASK_5BIT  0x000001F
#define MASK_10BIT 0x00003FF
#define MASK_16BIT 0x000FFFF
#define MASK_26BIT 0x3FFFFFF
#define MASK_28BIT 0xFFFFFFF

namespace redsea {

uint16_t syndrome(int vec) {

  uint16_t synd_reg = 0x000;
  int bit,l;

  for (int k=25; k>=0; k--) {
    bit       = (vec & (1 << k));
    l         = (synd_reg & 0x200);      // Store lefmost bit of register
    synd_reg  = (synd_reg << 1) & 0x3FF; // Rotate register
    synd_reg ^= (bit ? 0x31B : 0x00);    // Premultiply input by x^325 mod g(x)
    synd_reg ^= (l   ? 0x1B9 : 0x00);    // Division mod 2 by g(x)
  }

  return synd_reg;
}

BlockStream::BlockStream() : has_sync_for_(5), group_data_(4), has_block_(5),
  bitcount_(0), left_to_read_(0), bit_stream_(), wideblock_(0), block_has_errors_(50)

{

  offset_word_ = {0x0FC, 0x198, 0x168, 0x350, 0x1B4};

  block_for_offset_ = {0, 1, 2, 2, 3};

  error_lookup_ = {
    {0x200, 0b1000000000000000},
    {0x300, 0b1100000000000000},
    {0x180, 0b0110000000000000},
    {0x0c0, 0b0011000000000000},
    {0x060, 0b0001100000000000},
    {0x030, 0b0000110000000000},
    {0x018, 0b0000011000000000},
    {0x00c, 0b0000001100000000},
    {0x006, 0b0000000110000000},
    {0x003, 0b0000000011000000},
    {0x2dd, 0b0000000001100000},
    {0x3b2, 0b0000000000110000},
    {0x1d9, 0b0000000000011000},
    {0x230, 0b0000000000001100},
    {0x118, 0b0000000000000110},
    {0x08c, 0b0000000000000011},
    {0x046, 0b0000000000000001}
  };
}

void BlockStream::uncorrectable() {
  printf(":offset %d not received\n",expected_offset_);
  int data_length = 0;

  // TODO: return partial group
  /*if (has_block_[A]) {
    data_length = 1;

    if (has_block_[B]) {
      data_length = 2;

      if (has_block_[C] || has_block_[CI]) {
        data_length = 3;
      }
    }
  }*/

  block_has_errors_[block_counter_ % block_has_errors_.size()] = true;

  int erroneous_blocks = 0;
  for (bool e : block_has_errors_) {
    if (e)
      erroneous_blocks ++;
  }

  // Sync is lost when >45 out of last 50 blocks are erroneous (Section C.1.2)
  if (is_in_sync_ && erroneous_blocks > 45) {
    is_in_sync_ = false;
    for (int i=0; i<(int)block_has_errors_.size(); i++)
      block_has_errors_[i] = false;
    pi_ = 0x0000;
    printf(":too many errors, sync lost\n");
  }

  for (int o=A; o<=D; o++)
    has_block_[o] = false;

}

std::vector<uint16_t> BlockStream::getNextGroup() {

  has_whole_group_ = false;

  while (!has_whole_group_ && !isEOF()) {

    // Compensate for clock slip corrections
    bitcount_ += 26 - left_to_read_;

    // Read from radio
    for (int i=0; i < (is_in_sync_ ? left_to_read_ : 1); i++, bitcount_++) {
      wideblock_ = (wideblock_ << 1) + bit_stream_.getNextBit();
    }

    left_to_read_ = 26;
    wideblock_ &= MASK_28BIT;

    int block = (wideblock_ >> 1) & MASK_26BIT;

    // Find the offsets for which the syndrome is zero
    bool has_sync_for_any = false;
    for (int o=A; o<=D; o++) {
      has_sync_for_[o] = (syndrome(block ^ offset_word_[o]) == 0x000);
      has_sync_for_any |= has_sync_for_[o];
    }

    // Acquire sync

    if (!is_in_sync_) {
      if (has_sync_for_any) {
        for (int o=A; o<=D; o++) {
          if (has_sync_for_[o]) {
            int dist = bitcount_ - prevbitcount_;

            if (dist % 26 == 0 && dist <= 156 &&
                (block_for_offset_[prevsync_] + dist/26) % 4 == block_for_offset_[o]) {
              is_in_sync_ = true;
              expected_offset_ = o;
              printf(":sync!\n");
            } else {
              prevbitcount_ = bitcount_;
              prevsync_ = o;
            }
          }
        }
      }
    }

    // Synchronous decoding

    if (is_in_sync_) {

      block_counter_ ++;
      uint16_t message = block >> 10;

      if (expected_offset_ == C && !has_sync_for_[C] && has_sync_for_[CI]) {
        expected_offset_ = CI;
      }

      if ( !has_sync_for_[expected_offset_]) {

        // If message is a correct PI, error was probably in check bits
        if (expected_offset_ == A && message == pi_ && pi_ != 0) {
          has_sync_for_[A] = true;
          printf(":ignoring error in check bits\n");
        } else if (expected_offset_ == C && message == pi_ && pi_ != 0) {
          has_sync_for_[CI] = true;
          printf(":ignoring error in check bits\n");

        // Detect & correct clock slips (Section C.1.2)
        } else if (expected_offset_ == A && pi_ != 0 &&
            ((wideblock_ >> 12) & MASK_16BIT) == pi_) {
          message = pi_;
          wideblock_ >>= 1;
          has_sync_for_[A] = true;
          printf(":clock slip corrected\n");
        } else if (expected_offset_ == A && pi_ != 0 &&
            ((wideblock_ >> 10) & MASK_16BIT) == pi_) {
          message = pi_;
          wideblock_ = (wideblock_ << 1) + bit_stream_.getNextBit();
          has_sync_for_[A] = true;
          left_to_read_ = 25;
          printf(":clock slip corrected\n");

        // Detect & correct burst errors (Section B.2.2)
        } else {

          uint16_t synd_reg = syndrome(block ^ offset_word_[expected_offset_]);

          if (pi_ != 0 && expected_offset_ == A) {
            printf(":expecting PI%04x, got %04x, xor %d, syndrome %03x\n",
                pi_, block>>10, pi_ ^ (block>>10), synd_reg);
          }

          if (error_lookup_.find(synd_reg) != error_lookup_.end()) {
            message = (block >> 10) ^ error_lookup_[synd_reg];
            has_sync_for_[expected_offset_] = true;

            printf(":error corrected block %d using vector %04x for syndrome %03x\n",
                expected_offset_, error_lookup_[synd_reg], synd_reg);

          }

        }

        // Still no sync pulse
        if ( !has_sync_for_[expected_offset_]) {
          uncorrectable();
        }
      }

      // Error-free block received

      if (has_sync_for_[expected_offset_]) {

        group_data_[block_for_offset_[expected_offset_]] = message;
        has_block_[expected_offset_] = true;

        if (expected_offset_ == A) {
          pi_ = message;
        }

        // Complete group received
        if (has_block_[A] && has_block_[B] && (has_block_[C] || has_block_[CI]) && has_block_[D]) {
          has_whole_group_ = true;
        }
      }

      expected_offset_ = (expected_offset_ == C ? D : (expected_offset_ + 1) % 5);

      if (expected_offset_ == A) {
        for (int o=A; o<=D; o++)
          has_block_[o] = false;
      }

    }

  }

  return group_data_;

}

bool BlockStream::isEOF() const {
  return bit_stream_.isEOF();
}

} // namespace redsea
<|endoftext|>


<commit_before>// Copyright Daniel Wallin 2008. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef LUABIND_FORMAT_SIGNATURE_081014_HPP
# define LUABIND_FORMAT_SIGNATURE_081014_HPP

#include <luabind/config.hpp>
#include <luabind/lua_include.hpp>
#include <luabind/typeid.hpp>
#include <luabind/detail/meta.hpp>

namespace luabind {
	namespace adl {

		class object;
		class argument;
		template <class Base>
		struct table;

	} // namespace adl

	using adl::object;
	using adl::argument;
	using adl::table;

	namespace detail {

		LUABIND_API std::string get_class_name(lua_State* L, type_id const& i);

		template <class T, class Enable = void>
		struct type_to_string
		{
			static void get(lua_State* L)
			{
				lua_pushstring(L, get_class_name(L, typeid(T)).c_str());
			}
		};

		template <class T>
		struct type_to_string<T*>
		{
			static void get(lua_State* L)
			{
				type_to_string<T>::get(L);
				lua_pushstring(L, "*");
				lua_concat(L, 2);
			}
		};

		template <class T>
		struct type_to_string<T&>
		{
			static void get(lua_State* L)
			{
				type_to_string<T>::get(L);
				lua_pushstring(L, "&");
				lua_concat(L, 2);
			}
		};

		template <class T>
		struct type_to_string<T const>
		{
			static void get(lua_State* L)
			{
				type_to_string<T>::get(L);
				lua_pushstring(L, " const");
				lua_concat(L, 2);
			}
		};

# define LUABIND_TYPE_TO_STRING(x) \
    template <> \
    struct type_to_string<x> \
    { \
        static void get(lua_State* L) \
        { \
            lua_pushstring(L, #x); \
        } \
    };

# define LUABIND_INTEGRAL_TYPE_TO_STRING(x) \
    LUABIND_TYPE_TO_STRING(x) \
    LUABIND_TYPE_TO_STRING(unsigned x)

		LUABIND_INTEGRAL_TYPE_TO_STRING(char)
			LUABIND_INTEGRAL_TYPE_TO_STRING(short)
			LUABIND_INTEGRAL_TYPE_TO_STRING(int)
			LUABIND_INTEGRAL_TYPE_TO_STRING(long)

			LUABIND_TYPE_TO_STRING(void)
			LUABIND_TYPE_TO_STRING(bool)
			LUABIND_TYPE_TO_STRING(std::string)
			LUABIND_TYPE_TO_STRING(lua_State)

			LUABIND_TYPE_TO_STRING(luabind::object)
			LUABIND_TYPE_TO_STRING(luabind::argument)

# undef LUABIND_INTEGRAL_TYPE_TO_STRING
# undef LUABIND_TYPE_TO_STRING

			template <class Base>
		struct type_to_string<table<Base> >
		{
			static void get(lua_State* L)
			{
				lua_pushstring(L, "table");
			}
		};

		inline void format_signature_aux(lua_State*, bool, meta::type_list< >)
		{}

		template <class Signature>
		void format_signature_aux(lua_State* L, bool first, Signature)
		{
			if(!first)
				lua_pushstring(L, ",");
			type_to_string<typename meta::front<Signature>::type>::get(L);
			format_signature_aux(L, false, typename meta::pop_front<Signature>::type());
		}

		template <class Signature>
		void format_signature(lua_State* L, char const* function, Signature)
		{
			using first = typename meta::front<Signature>::type;

			type_to_string<first>::get(L);

			lua_pushstring(L, " ");
			lua_pushstring(L, function);

			lua_pushstring(L, "(");
			format_signature_aux(
				L
				, true
				, typename meta::pop_front<Signature>::type()
			);
			lua_pushstring(L, ")");
			size_t sz = meta::size<Signature>::value;
			size_t ncat = sz * 2 + 2;
			if(sz == 1)
				++ncat;
			lua_concat(L, ncat);
		}

	} // namespace detail

} // namespace luabind

#endif // LUABIND_FORMAT_SIGNATURE_081014_HPP

<commit_msg>Fixed a "dead code" warning of Coverity Scan<commit_after>// Copyright Daniel Wallin 2008. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef LUABIND_FORMAT_SIGNATURE_081014_HPP
# define LUABIND_FORMAT_SIGNATURE_081014_HPP

#include <luabind/config.hpp>
#include <luabind/lua_include.hpp>
#include <luabind/typeid.hpp>
#include <luabind/detail/meta.hpp>

namespace luabind {
	namespace adl {

		class object;
		class argument;
		template <class Base>
		struct table;

	} // namespace adl

	using adl::object;
	using adl::argument;
	using adl::table;

	namespace detail {

		LUABIND_API std::string get_class_name(lua_State* L, type_id const& i);

		template <class T, class Enable = void>
		struct type_to_string
		{
			static void get(lua_State* L)
			{
				lua_pushstring(L, get_class_name(L, typeid(T)).c_str());
			}
		};

		template <class T>
		struct type_to_string<T*>
		{
			static void get(lua_State* L)
			{
				type_to_string<T>::get(L);
				lua_pushstring(L, "*");
				lua_concat(L, 2);
			}
		};

		template <class T>
		struct type_to_string<T&>
		{
			static void get(lua_State* L)
			{
				type_to_string<T>::get(L);
				lua_pushstring(L, "&");
				lua_concat(L, 2);
			}
		};

		template <class T>
		struct type_to_string<T const>
		{
			static void get(lua_State* L)
			{
				type_to_string<T>::get(L);
				lua_pushstring(L, " const");
				lua_concat(L, 2);
			}
		};

# define LUABIND_TYPE_TO_STRING(x) \
    template <> \
    struct type_to_string<x> \
    { \
        static void get(lua_State* L) \
        { \
            lua_pushstring(L, #x); \
        } \
    };

# define LUABIND_INTEGRAL_TYPE_TO_STRING(x) \
    LUABIND_TYPE_TO_STRING(x) \
    LUABIND_TYPE_TO_STRING(unsigned x)

		LUABIND_INTEGRAL_TYPE_TO_STRING(char)
			LUABIND_INTEGRAL_TYPE_TO_STRING(short)
			LUABIND_INTEGRAL_TYPE_TO_STRING(int)
			LUABIND_INTEGRAL_TYPE_TO_STRING(long)

			LUABIND_TYPE_TO_STRING(void)
			LUABIND_TYPE_TO_STRING(bool)
			LUABIND_TYPE_TO_STRING(std::string)
			LUABIND_TYPE_TO_STRING(lua_State)

			LUABIND_TYPE_TO_STRING(luabind::object)
			LUABIND_TYPE_TO_STRING(luabind::argument)

# undef LUABIND_INTEGRAL_TYPE_TO_STRING
# undef LUABIND_TYPE_TO_STRING

			template <class Base>
		struct type_to_string<table<Base> >
		{
			static void get(lua_State* L)
			{
				lua_pushstring(L, "table");
			}
		};

		inline void format_signature_aux(lua_State*, bool, meta::type_list< >)
		{}

		template <class Signature>
		void format_signature_aux(lua_State* L, bool first, Signature)
		{
			if(!first)
				lua_pushstring(L, ",");
			type_to_string<typename meta::front<Signature>::type>::get(L);
			format_signature_aux(L, false, typename meta::pop_front<Signature>::type());
		}

		template <class Signature>
		void format_signature(lua_State* L, char const* function, Signature)
		{
			using first = typename meta::front<Signature>::type;

			type_to_string<first>::get(L);

			lua_pushstring(L, " ");
			lua_pushstring(L, function);

			lua_pushstring(L, "(");
			format_signature_aux(
				L
				, true
				, typename meta::pop_front<Signature>::type()
			);
			lua_pushstring(L, ")");
			constexpr size_t ncat = meta::size<Signature>::value * 2 + 2 + (meta::size<Signature>::value == 1 ? 1 : 0);
			lua_concat(L, static_cast<int>(ncat));
		}

	} // namespace detail

} // namespace luabind

#endif // LUABIND_FORMAT_SIGNATURE_081014_HPP

<|endoftext|>


<commit_before>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2014 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include <gtest/gtest.h>

#include "OgreRoot.h"
#include "OgreSceneNode.h"
#include "OgreEntity.h"
#include "OgreCamera.h"
#include "RootWithoutRenderSystemFixture.h"

#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
#include <random>
using std::minstd_rand;
#else
#include <tr1/random>
using std::tr1::minstd_rand;
#endif

using namespace Ogre;

TEST(Root,shutdown)
{
    Root root;
    root.shutdown();
}

TEST(SceneManager,removeAndDestroyAllChildren)
{
    Root root;
    SceneManager* sm = root.createSceneManager();
    sm->getRootSceneNode()->createChildSceneNode();
    sm->getRootSceneNode()->createChildSceneNode();
    sm->getRootSceneNode()->removeAndDestroyAllChildren();
}

static void createRandomEntityClones(Entity* ent, size_t cloneCount, const Vector3& min,
                                     const Vector3& max, SceneManager* mgr)
{
    // we want cross platform consistent sequence
    minstd_rand rng;

    for (size_t n = 0; n < cloneCount; ++n)
    {
        // Create a new node under the root.
        SceneNode* node = mgr->createSceneNode();
        // Random translate.
        Vector3 nodePos = max - min;
        nodePos.x *= float(rng())/rng.max();
        nodePos.y *= float(rng())/rng.max();
        nodePos.z *= float(rng())/rng.max();
        nodePos += min;
        node->setPosition(nodePos);
        mgr->getRootSceneNode()->addChild(node);
        Entity* cloneEnt = ent->clone(StringConverter::toString(n));
        // Attach to new node.
        node->attachObject(cloneEnt);
    }
}

struct SceneQueryTest : public RootWithoutRenderSystemFixture {
    SceneManager* mSceneMgr;
    Camera* mCamera;
    SceneNode* mCameraNode;

    void SetUp() {
        RootWithoutRenderSystemFixture::SetUp();

        mSceneMgr = mRoot->createSceneManager();
        mCamera = mSceneMgr->createCamera("Camera");
        mCameraNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
        mCameraNode->attachObject(mCamera);
        mCameraNode->setPosition(0,0,500);
        mCameraNode->lookAt(Vector3(0, 0, 0), Node::TS_PARENT);

        // Create a set of random balls
        Entity* ent = mSceneMgr->createEntity("501", "sphere.mesh", "General");

        // stick one at the origin so one will always be hit by ray
        mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);
        createRandomEntityClones(ent, 500, Vector3(-2500,-2500,-2500), Vector3(2500,2500,2500), mSceneMgr);

        mSceneMgr->_updateSceneGraph(mCamera);
    }
};

TEST_F(SceneQueryTest,Intersection)
{
    IntersectionSceneQuery* intersectionQuery = mSceneMgr->createIntersectionQuery();

    int expected[][2] = {
        {0, 391},   {1, 8},     {117, 128}, {118, 171}, {118, 24},  {121, 72},  {121, 95},
        {132, 344}, {14, 227},  {14, 49},   {144, 379}, {151, 271}, {153, 28},  {164, 222},
        {169, 212}, {176, 20},  {179, 271}, {185, 238}, {190, 47},  {193, 481}, {201, 210},
        {205, 404}, {235, 366}, {239, 3},   {250, 492}, {256, 67},  {26, 333},  {260, 487},
        {263, 272}, {265, 319}, {265, 472}, {270, 45},  {284, 329}, {289, 405}, {316, 80},
        {324, 388}, {334, 337}, {336, 436}, {34, 57},   {340, 440}, {342, 41},  {348, 82},
        {35, 478},  {372, 412}, {380, 460}, {398, 92},  {417, 454}, {432, 99},  {448, 79},
        {498, 82},  {72, 77}
    };

    IntersectionSceneQueryResult& results = intersectionQuery->execute();
    EXPECT_EQ(results.movables2movables.size(), sizeof(expected)/sizeof(expected[0]));

    int i = 0;
    for (SceneQueryMovableIntersectionList::iterator mov = results.movables2movables.begin();
         mov != results.movables2movables.end(); ++mov)
    {
        SceneQueryMovableObjectPair& thepair = *mov;
        // printf("{%d, %d},", StringConverter::parseInt(thepair.first->getName()), StringConverter::parseInt(thepair.second->getName()));
        ASSERT_EQ(expected[i][0], StringConverter::parseInt(thepair.first->getName()));
        ASSERT_EQ(expected[i][1], StringConverter::parseInt(thepair.second->getName()));
        i++;
    }
    // printf("\n");
}

TEST_F(SceneQueryTest, Ray) {
    RaySceneQuery* rayQuery = mSceneMgr->createRayQuery(mCamera->getCameraToViewportRay(0.5, 0.5));
    rayQuery->setSortByDistance(true, 2);

    RaySceneQueryResult& results = rayQuery->execute();

    ASSERT_EQ("501", results[0].movable->getName());
    ASSERT_EQ("397", results[1].movable->getName());
}
<commit_msg>Tests: fix building on old MSVC<commit_after>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2014 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include <gtest/gtest.h>

#include "OgreRoot.h"
#include "OgreSceneNode.h"
#include "OgreEntity.h"
#include "OgreCamera.h"
#include "RootWithoutRenderSystemFixture.h"

#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600)
#include <random>
using std::minstd_rand;
#else
#include <tr1/random>
using std::tr1::minstd_rand;
#endif

using namespace Ogre;

TEST(Root,shutdown)
{
    Root root;
    root.shutdown();
}

TEST(SceneManager,removeAndDestroyAllChildren)
{
    Root root;
    SceneManager* sm = root.createSceneManager();
    sm->getRootSceneNode()->createChildSceneNode();
    sm->getRootSceneNode()->createChildSceneNode();
    sm->getRootSceneNode()->removeAndDestroyAllChildren();
}

static void createRandomEntityClones(Entity* ent, size_t cloneCount, const Vector3& min,
                                     const Vector3& max, SceneManager* mgr)
{
    // we want cross platform consistent sequence
    minstd_rand rng;

    for (size_t n = 0; n < cloneCount; ++n)
    {
        // Create a new node under the root.
        SceneNode* node = mgr->createSceneNode();
        // Random translate.
        Vector3 nodePos = max - min;
        nodePos.x *= float(rng())/rng.max();
        nodePos.y *= float(rng())/rng.max();
        nodePos.z *= float(rng())/rng.max();
        nodePos += min;
        node->setPosition(nodePos);
        mgr->getRootSceneNode()->addChild(node);
        Entity* cloneEnt = ent->clone(StringConverter::toString(n));
        // Attach to new node.
        node->attachObject(cloneEnt);
    }
}

struct SceneQueryTest : public RootWithoutRenderSystemFixture {
    SceneManager* mSceneMgr;
    Camera* mCamera;
    SceneNode* mCameraNode;

    void SetUp() {
        RootWithoutRenderSystemFixture::SetUp();

        mSceneMgr = mRoot->createSceneManager();
        mCamera = mSceneMgr->createCamera("Camera");
        mCameraNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
        mCameraNode->attachObject(mCamera);
        mCameraNode->setPosition(0,0,500);
        mCameraNode->lookAt(Vector3(0, 0, 0), Node::TS_PARENT);

        // Create a set of random balls
        Entity* ent = mSceneMgr->createEntity("501", "sphere.mesh", "General");

        // stick one at the origin so one will always be hit by ray
        mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);
        createRandomEntityClones(ent, 500, Vector3(-2500,-2500,-2500), Vector3(2500,2500,2500), mSceneMgr);

        mSceneMgr->_updateSceneGraph(mCamera);
    }
};

TEST_F(SceneQueryTest,Intersection)
{
    IntersectionSceneQuery* intersectionQuery = mSceneMgr->createIntersectionQuery();

    int expected[][2] = {
        {0, 391},   {1, 8},     {117, 128}, {118, 171}, {118, 24},  {121, 72},  {121, 95},
        {132, 344}, {14, 227},  {14, 49},   {144, 379}, {151, 271}, {153, 28},  {164, 222},
        {169, 212}, {176, 20},  {179, 271}, {185, 238}, {190, 47},  {193, 481}, {201, 210},
        {205, 404}, {235, 366}, {239, 3},   {250, 492}, {256, 67},  {26, 333},  {260, 487},
        {263, 272}, {265, 319}, {265, 472}, {270, 45},  {284, 329}, {289, 405}, {316, 80},
        {324, 388}, {334, 337}, {336, 436}, {34, 57},   {340, 440}, {342, 41},  {348, 82},
        {35, 478},  {372, 412}, {380, 460}, {398, 92},  {417, 454}, {432, 99},  {448, 79},
        {498, 82},  {72, 77}
    };

    IntersectionSceneQueryResult& results = intersectionQuery->execute();
    EXPECT_EQ(results.movables2movables.size(), sizeof(expected)/sizeof(expected[0]));

    int i = 0;
    for (SceneQueryMovableIntersectionList::iterator mov = results.movables2movables.begin();
         mov != results.movables2movables.end(); ++mov)
    {
        SceneQueryMovableObjectPair& thepair = *mov;
        // printf("{%d, %d},", StringConverter::parseInt(thepair.first->getName()), StringConverter::parseInt(thepair.second->getName()));
        ASSERT_EQ(expected[i][0], StringConverter::parseInt(thepair.first->getName()));
        ASSERT_EQ(expected[i][1], StringConverter::parseInt(thepair.second->getName()));
        i++;
    }
    // printf("\n");
}

TEST_F(SceneQueryTest, Ray) {
    RaySceneQuery* rayQuery = mSceneMgr->createRayQuery(mCamera->getCameraToViewportRay(0.5, 0.5));
    rayQuery->setSortByDistance(true, 2);

    RaySceneQueryResult& results = rayQuery->execute();

    ASSERT_EQ("501", results[0].movable->getName());
    ASSERT_EQ("397", results[1].movable->getName());
}
<|endoftext|>


<commit_before>// Catch
#include <catch.hpp>
#include <helper.hpp>

// C++ Standard Library
#include <cmath>
#include <array>

// Mantella
#include <mantella>

TEST_CASE("geometry: get2DRotationMatrix(...)", "") {
  SECTION("Generates 2D rotation matrix") {
    const std::array<double, 15>& angles = {{0.0,  45.0, 90.0, 135.0, 180.0, 225.0, 270.0, 315.0, 360.0, -0.0, -45.0, -90.0, -180.0, -225.0, -315.0}};

    for (const auto& angle : angles) {
      arma::Mat<double>::fixed<2, 2> expected({
        std::cos(angle), -std::sin(angle),
        std::sin(angle), std::cos(angle)
      });

      compare(mant::get2DRotationMatrix(angle), expected);
    }
  }
}

TEST_CASE("geometry: get3DRotationMatrix(...)", "") {
  SECTION("Generates 3D rotation matrix") {
    const std::array<double, 14>& rollAngles = {{0.0, 45.0, 90.0, 135.0, 180.0, 225, 270, 315, 360, -0, -45, 276, -56, -45.89}};
    const std::array<double, 14>& pitchAngles = {{0.0, 45.0, 90.0, 135.0, 180.0, 225, 270, 315, 360, -0, -90, -89, 78, -245}};
    const std::array<double, 14>& yawAngles = {{0.0, 45.0, 90.0, 135.0, 180.0, 225, 270, 315, 360, -0, -225, -310, -90, 345}};

    for (const auto& rollAngle : rollAngles) {
      for (const auto& pitchAngle : pitchAngles) {
        for (const auto& yawAngle : yawAngles) {
          arma::Mat<double>::fixed<3, 3> expectedRoll({
            1.0, 0.0, 0.0,
            0.0, std::cos(rollAngle), -std::sin(rollAngle),
            0.0, std::sin(rollAngle), std::cos(rollAngle),
          });

          arma::Mat<double>::fixed<3, 3> expectedPitch({
            std::cos(pitchAngle), 0.0, std::sin(pitchAngle),
            0.0, 1.0, 0.0,
            -std::sin(pitchAngle), 0.0, std::cos(pitchAngle)
          });

          arma::Mat<double>::fixed<3, 3> expectedYaw({
            std::cos(yawAngle), -std::sin(yawAngle), 0.0,
            std::sin(yawAngle), std::cos(yawAngle), 0.0,
            0.0, 0.0, 1.0
          });

          compare<double>(mant::get3DRotationMatrix(rollAngle, pitchAngle, yawAngle), expectedRoll * expectedPitch * expectedYaw);
        }
      }
    }
  }
}

TEST_CASE("geometry: getCircleCircleIntersection(...)", "") {
  SECTION("Finds intersection") {
    arma::Col<double>::fixed<2> expected;

    expected = {-2.5522451636, 1.8231290303};
    compare(mant::getCircleCircleIntersection({-2.0, 3.0}, 1.3, {-1.5, 2.4}, 1.2), expected);

    expected = {-0.1804143359, 0.9753358195};
    compare(mant::getCircleCircleIntersection({1.8, -2.5}, 4.0, {-3.0, 2.0}, 3.0), expected);
  }

  SECTION("Throws an exception, if both centers are on the same spot") {
    CHECK_THROWS_AS(mant::getCircleCircleIntersection({-2.0, 3.0}, 1.3, {-2.0, 3.0}, 1.3), std::logic_error);
  }

  SECTION("Throws an exception, if the circles are to far apart") {
    CHECK_THROWS_AS(mant::getCircleCircleIntersection({-8.0, 3.0}, 1.3, {-2.0, 3.0}, 1.3), std::logic_error);
  }

  SECTION("Throws an exception, if a circle is enclosed by the other one") {
    CHECK_THROWS_AS(mant::getCircleCircleIntersection({-2.0, 2.8}, 0.3, {-2.0, 3.0}, 1.3), std::logic_error);
  }
}

TEST_CASE("geometry: getCircleSphereIntersection(...)", "") {
  SECTION("Finds intersection") {
    arma::Col<double>::fixed<3> expected;

    expected = {-2.095, 1.1962336728, 0.0};
    compare(mant::getCircleSphereIntersection({-2.0, 0.0, 0.0}, 1.2, {0.0, 0.0, 1.0}, {-3.0, 0.0, 0.0}, 1.5), expected);

    expected = {-2.5522451636, 1.8231290303, 0.0};
    compare(mant::getCircleSphereIntersection({-2.0, 3.0, 0.0}, 1.3, {0.0, 0.0, 1.0}, {-1.5, 2.4, 0.0}, 1.2), expected);

    expected = {-0.1804143359, 0.9753358195, 0.0};
    compare(mant::getCircleSphereIntersection({1.8, -2.5, 0.0}, 4.0, {0.0, 0.0, 1.0}, {-3.0, 2.0, 0.0}, 3.0), expected);
  }
}

<commit_msg>test: Added exception tests for geometry.hpp<commit_after>// Catch
#include <catch.hpp>
#include <helper.hpp>

// C++ Standard Library
#include <cmath>
#include <array>

// Mantella
#include <mantella>

TEST_CASE("geometry: get2DRotationMatrix(...)", "") {
  SECTION("Generates 2D rotation matrix") {
    const std::array<double, 15>& angles = {{0.0,  45.0, 90.0, 135.0, 180.0, 225.0, 270.0, 315.0, 360.0, -0.0, -45.0, -90.0, -180.0, -225.0, -315.0}};

    for (const auto& angle : angles) {
      arma::Mat<double>::fixed<2, 2> expected({
        std::cos(angle), -std::sin(angle),
        std::sin(angle), std::cos(angle)
      });

      compare(mant::get2DRotationMatrix(angle), expected);
    }
  }
}

TEST_CASE("geometry: get3DRotationMatrix(...)", "") {
  SECTION("Generates 3D rotation matrix") {
    const std::array<double, 14>& rollAngles = {{0.0, 45.0, 90.0, 135.0, 180.0, 225, 270, 315, 360, -0, -45, 276, -56, -45.89}};
    const std::array<double, 14>& pitchAngles = {{0.0, 45.0, 90.0, 135.0, 180.0, 225, 270, 315, 360, -0, -90, -89, 78, -245}};
    const std::array<double, 14>& yawAngles = {{0.0, 45.0, 90.0, 135.0, 180.0, 225, 270, 315, 360, -0, -225, -310, -90, 345}};

    for (const auto& rollAngle : rollAngles) {
      for (const auto& pitchAngle : pitchAngles) {
        for (const auto& yawAngle : yawAngles) {
          arma::Mat<double>::fixed<3, 3> expectedRoll({
            1.0, 0.0, 0.0,
            0.0, std::cos(rollAngle), -std::sin(rollAngle),
            0.0, std::sin(rollAngle), std::cos(rollAngle),
          });

          arma::Mat<double>::fixed<3, 3> expectedPitch({
            std::cos(pitchAngle), 0.0, std::sin(pitchAngle),
            0.0, 1.0, 0.0,
            -std::sin(pitchAngle), 0.0, std::cos(pitchAngle)
          });

          arma::Mat<double>::fixed<3, 3> expectedYaw({
            std::cos(yawAngle), -std::sin(yawAngle), 0.0,
            std::sin(yawAngle), std::cos(yawAngle), 0.0,
            0.0, 0.0, 1.0
          });

          compare<double>(mant::get3DRotationMatrix(rollAngle, pitchAngle, yawAngle), expectedRoll * expectedPitch * expectedYaw);
        }
      }
    }
  }
}

TEST_CASE("geometry: getCircleCircleIntersection(...)", "") {
  SECTION("Finds intersection") {
    arma::Col<double>::fixed<2> expected;

    expected = {-2.5522451636, 1.8231290303};
    compare(mant::getCircleCircleIntersection({-2.0, 3.0}, 1.3, {-1.5, 2.4}, 1.2), expected);

    expected = {-0.1804143359, 0.9753358195};
    compare(mant::getCircleCircleIntersection({1.8, -2.5}, 4.0, {-3.0, 2.0}, 3.0), expected);
  }

  SECTION("Throws an exception, if both centers are on the same spot") {
    CHECK_THROWS_AS(mant::getCircleCircleIntersection({-2.0, 3.0}, 1.3, {-2.0, 3.0}, 1.3), std::logic_error);
  }

  SECTION("Throws an exception, if the circles are to far apart") {
    CHECK_THROWS_AS(mant::getCircleCircleIntersection({-8.0, 3.0}, 1.3, {-2.0, 3.0}, 1.3), std::logic_error);
  }

  SECTION("Throws an exception, if the first circle is enclosed by second one") {
    CHECK_THROWS_AS(mant::getCircleCircleIntersection({-4.0, 2.8}, 0.3, {-4.0, 3.0}, 1.3), std::logic_error);
  }

  SECTION("Throws an exception, if the second circle is enclosed by first one") {
    CHECK_THROWS_AS(mant::getCircleCircleIntersection({6.8, 1.4}, 2.0, {6.5, 1.2}, 0.1), std::logic_error);
  }

  SECTION("Throws an exception, if any radius is 0 or negative.") {
    CHECK_THROWS_AS(mant::getCircleCircleIntersection({1.8, -2.5}, 0.0, {1.8, 0.5}, 3.0), std::logic_error);
    CHECK_THROWS_AS(mant::getCircleCircleIntersection({1.8, -2.5}, 4.0, {-3.0, 2.0}, -3.0), std::logic_error);
  }
}

TEST_CASE("geometry: getCircleSphereIntersection(...)", "") {
  SECTION("Finds intersection") {
    arma::Col<double>::fixed<3> expected;

    expected = {-2.095, 1.1962336728, 0.0};
    compare(mant::getCircleSphereIntersection({-2.0, 0.0, 0.0}, 1.2, {0.0, 0.0, 1.0}, {-3.0, 0.0, 0.0}, 1.5), expected);

    expected = {-2.5522451636, 1.8231290303, 0.0};
    compare(mant::getCircleSphereIntersection({-2.0, 3.0, 0.0}, 1.3, {0.0, 0.0, 1.0}, {-1.5, 2.4, 0.0}, 1.2), expected);

    expected = {-0.1804143359, 0.9753358195, 0.0};
    compare(mant::getCircleSphereIntersection({1.8, -2.5, 0.0}, 4.0, {0.0, 0.0, 1.0}, {-3.0, 2.0, 0.0}, 3.0), expected);
  }

  SECTION("Throws an exception, if both centers are on the same spot") {
    CHECK_THROWS_AS(mant::getCircleSphereIntersection({-2.0, 0.0, 0.0}, 1.2, {0.0, 0.0, 1.0}, {-2.0, 0.0, 0.0}, 1.5), std::logic_error);
  }

  SECTION("Throws an exception, if the circle and sphere are to far apart") {
    CHECK_THROWS_AS(mant::getCircleSphereIntersection({-7.0, 3.0, 0.0}, 1.3, {0.0, 0.0, 1.0}, {-1.5, 2.4, 0.0}, 1.2), std::logic_error);
  }

  SECTION("Throws an exception, if the circle is enclosed by the sphere") {
    CHECK_THROWS_AS(mant::getCircleSphereIntersection({-2.0, 3.0, 0.0}, 1.3, {0.0, 1.0, 1.0}, {-1.5, 2.4, 0.0}, 5.0), std::logic_error);
  }

  SECTION("Throws an exception, if the sphere is enclosed by the circle") {
    CHECK_THROWS_AS(mant::getCircleSphereIntersection({-2.0, 3.0, 0.0}, 6.3, {0.0, 1.0, 1.0}, {-1.5, 2.4, 0.0}, 0.2), std::logic_error);
  }

  SECTION("Throws an exception, if any radius is 0 or negative.") {
    CHECK_THROWS_AS(mant::getCircleSphereIntersection({-1.5, 3.4, 0.0}, 1.0, {0.0, 0.0, 1.0}, {-1.5, 2.4, 0.0}, 0.0), std::logic_error);
    CHECK_THROWS_AS(mant::getCircleSphereIntersection({1.8, -2.5, 0.0}, -4.0, {0.0, 0.0, 1.0}, {-3.0, 2.0, 0.0}, 3.0), std::logic_error);
  }
}

<|endoftext|>


<commit_before>#include "alloGLV/al_ControlGLV.hpp"

namespace al{

bool GLVInputControl::onMouseDown(const Mouse& m){
	glv::space_t xrel=m.x(), yrel=m.y();
	glv().setMouseDown(xrel,yrel, m.button(), 0);
	glv().setMousePos(m.x(), m.y(), xrel, yrel);
	return !glv().propagateEvent();
}

bool GLVInputControl::onMouseUp(const al::Mouse& m){
	glv::space_t xrel, yrel;
	glv().setMouseUp(xrel,yrel, m.button(), 0);
	glv().setMousePos(m.x(), m.y(), xrel, yrel);
	return !glv().propagateEvent();
}

bool GLVInputControl::keyToGLV(const al::Keyboard& k, bool down){
	down ? glv().setKeyDown(k.key()) : glv().setKeyUp(k.key());
	const_cast<glv::Keyboard*>(&glv().keyboard())->alt(k.alt());
	const_cast<glv::Keyboard*>(&glv().keyboard())->caps(k.caps());
	const_cast<glv::Keyboard*>(&glv().keyboard())->ctrl(k.ctrl());
	const_cast<glv::Keyboard*>(&glv().keyboard())->meta(k.meta());
	const_cast<glv::Keyboard*>(&glv().keyboard())->shift(k.shift());
	return glv().propagateEvent();
}

bool GLVWindowControl::onCreate(){
	glv().broadcastEvent(glv::Event::WindowCreate);
	return true;
}

bool GLVWindowControl::onDestroy(){
	glv().broadcastEvent(glv::Event::WindowDestroy);
	return true;
}

bool GLVWindowControl::onResize(int dw, int dh){
	glv().extent(glv().width() + dw, glv().height() + dh);
	//printf("GLVWindowControl onResize: %d %d %f %f\n", dw, dh, glv().width(), glv().height());
	glv().broadcastEvent(glv::Event::WindowResize);
	return true;
}

GLVDetachable::GLVDetachable()
:	glv::GLV(0,0),
	mParentWindow(NULL), mInputControl(*this), mWindowControl(*this)
{
	init();
}

GLVDetachable::GLVDetachable(Window& parent)
:	glv::GLV(0,0),
	mInputControl(*this), mWindowControl(*this)
{
	parentWindow(parent);
	init();
}

static void ntDetachedButton(const glv::Notification& n){
	GLVDetachable * R = n.receiver<GLVDetachable>();
	if(n.sender<glv::Button>()->getValue()){
		R->detached(true);
	}
	else{
		R->detached(false);
	}
}

void GLVDetachable::init(){
	mDetachedButton.attach(ntDetachedButton, glv::Update::Value, this);
	mDetachedButton.disable(glv::Momentary);
	mDetachedButton.symbolOn(glv::draw::viewChild);
	mDetachedButton.symbolOff(glv::draw::viewSibling);
	mDetachedButton.disable(glv::DrawBorder);
	stretch(1,1);
	this->disable(glv::DrawBack);
}

void GLVDetachable::addGUI(Window& w){
	w.prepend(mInputControl);
	w.append(mWindowControl);
}

void GLVDetachable::remGUI(Window& w){
	w.remove(mInputControl);
	w.remove(mWindowControl);
}

GLVDetachable& GLVDetachable::detached(bool v){
	if(v && !detached()){			// is not detached
		if(mParentWindow){
			remGUI(parentWindow());
		}
		glv::Rect ru = unionOfChildren();
		enable(glv::DrawBack);
		{
			glv::View * cv = child;
			while(cv){
				cv->posAdd(-ru.l, -ru.t);
				cv = cv->sibling;
			}
		}
		//ru.print();
		//posAdd(-ru.l, -ru.t);
		detachedWindow().create(Window::Dim(ru.w, ru.h));
		addGUI(detachedWindow());
	}
	else if(detached()){			// is currently detached, attach back to parent, if any
		remGUI(detachedWindow());
		detachedWindow().destroy();
		if(mParentWindow){
			disable(glv::DrawBack);
			pos(0,0);
			addGUI(parentWindow());
		}
	}
	return *this;
}

GLVDetachable& GLVDetachable::parentWindow(Window& v){
	if(&v != mParentWindow){
		if(!detached()){
			if(mParentWindow){
				remGUI(parentWindow());
			}
			mParentWindow = &v;
			disable(glv::DrawBack);
			addGUI(parentWindow());
			//printf("%d\n", parentWindow().created());
		}
		else{
			mParentWindow = &v;
		}
	}
	return *this;
}

} // al::
<commit_msg>fix problem with mouse x,y state when detaching GUI<commit_after>#include "alloGLV/al_ControlGLV.hpp"

namespace al{

bool GLVInputControl::onMouseDown(const Mouse& m){
	glv::space_t xrel=m.x(), yrel=m.y();
	glv().setMouseDown(xrel,yrel, m.button(), 0);
	glv().setMousePos(m.x(), m.y(), xrel, yrel);
	return !glv().propagateEvent();
}

bool GLVInputControl::onMouseUp(const al::Mouse& m){
	glv::space_t xrel, yrel;
	glv().setMouseUp(xrel,yrel, m.button(), 0);
	glv().setMousePos(m.x(), m.y(), xrel, yrel);
	return !glv().propagateEvent();
}

bool GLVInputControl::keyToGLV(const al::Keyboard& k, bool down){
	down ? glv().setKeyDown(k.key()) : glv().setKeyUp(k.key());
	const_cast<glv::Keyboard*>(&glv().keyboard())->alt(k.alt());
	const_cast<glv::Keyboard*>(&glv().keyboard())->caps(k.caps());
	const_cast<glv::Keyboard*>(&glv().keyboard())->ctrl(k.ctrl());
	const_cast<glv::Keyboard*>(&glv().keyboard())->meta(k.meta());
	const_cast<glv::Keyboard*>(&glv().keyboard())->shift(k.shift());
	return glv().propagateEvent();
}

bool GLVWindowControl::onCreate(){
	glv().broadcastEvent(glv::Event::WindowCreate);
	return true;
}

bool GLVWindowControl::onDestroy(){
	glv().broadcastEvent(glv::Event::WindowDestroy);
	return true;
}

bool GLVWindowControl::onResize(int dw, int dh){
	glv().extent(glv().width() + dw, glv().height() + dh);
	//printf("GLVWindowControl onResize: %d %d %f %f\n", dw, dh, glv().width(), glv().height());
	glv().broadcastEvent(glv::Event::WindowResize);
	return true;
}

GLVDetachable::GLVDetachable()
:	glv::GLV(0,0),
	mParentWindow(NULL), mInputControl(*this), mWindowControl(*this)
{
	init();
}

GLVDetachable::GLVDetachable(Window& parent)
:	glv::GLV(0,0),
	mInputControl(*this), mWindowControl(*this)
{
	parentWindow(parent);
	init();
}

static void ntDetachedButton(const glv::Notification& n){
	GLVDetachable * R = n.receiver<GLVDetachable>();
	//if(R->mouse().isDown()) return;
	if(n.sender<glv::Button>()->getValue()){
		R->detached(true);
	}
	else{
		R->detached(false);
	}
}

void GLVDetachable::init(){
	mDetachedButton.attach(ntDetachedButton, glv::Update::Value, this);
	mDetachedButton.disable(glv::Momentary);
	mDetachedButton.symbolOn(glv::draw::viewChild);
	mDetachedButton.symbolOff(glv::draw::viewSibling);
	mDetachedButton.disable(glv::DrawBorder);
	stretch(1,1);
	this->disable(glv::DrawBack);
}

void GLVDetachable::addGUI(Window& w){
	w.prepend(mInputControl);
	w.append(mWindowControl);
}

void GLVDetachable::remGUI(Window& w){
	w.remove(mInputControl);
	w.remove(mWindowControl);
}

GLVDetachable& GLVDetachable::detached(bool v){
	if(v && !detached()){			// is not detached
		if(mParentWindow){
			remGUI(parentWindow());
		}
		glv::Rect ru = unionOfChildren();
		enable(glv::DrawBack);
		{
			glv::View * cv = child;
			while(cv){
				cv->posAdd(-ru.l, -ru.t);
				cv = cv->sibling;
			}
		}
		//ru.print();
		//posAdd(-ru.l, -ru.t);
		//detachedWindow().create(Window::Dim(ru.w, ru.h));
		int pl=0, pt=0;
		if(mParentWindow){
			pl = parentWindow().dimensions().l;
			pt = parentWindow().dimensions().t;
			//printf("%d %d\n", pl, pt);
		}
		detachedWindow().create(Window::Dim(pl, pt, ru.w, ru.h));
		addGUI(detachedWindow());
	}
	else if(detached()){ // is currently detached, attach back to parent, if any
		remGUI(detachedWindow());
		detachedWindow().destroy();
		if(mParentWindow){
			disable(glv::DrawBack);
			pos(0,0);
			addGUI(parentWindow());
		}
	}

	// This is a hack to ensure all GLV mouse button states are "up" (false).
	// Because the GLV changes windows between mouse down and mouse up calls,
	// the mouse relative position gets into an inconsistent state on mouse up.
	const int mx = mouse().x();
	const int my = mouse().y();
	for(int i=0; i<GLV_MAX_MOUSE_BUTTONS; ++i){
		glv::space_t x=mx,y=my;
		setMouseUp(x,y, i, 1);
	}

	return *this;
}

GLVDetachable& GLVDetachable::parentWindow(Window& v){
	if(&v != mParentWindow){
		if(!detached()){
			if(mParentWindow){
				remGUI(parentWindow());
			}
			mParentWindow = &v;
			disable(glv::DrawBack);
			addGUI(parentWindow());
			//printf("%d\n", parentWindow().created());
		}
		else{
			mParentWindow = &v;
		}
	}
	return *this;
}

} // al::
<|endoftext|>