<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtOpenGL module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qtriangulatingstroker_p.h"
#include <qmath.h>

QT_BEGIN_NAMESPACE

#define CURVE_FLATNESS Q_PI / 8

void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *cur,
                                               bool implicitClose, bool endsAtStart)
{
    if (endsAtStart) {
        join(start + 2);
    } else if (implicitClose) {
        join(start);
        lineTo(start);
        join(start+2);
    } else {
        endCap(cur);
    }
    int count = m_vertices.size();
    m_vertices.add(m_vertices.at(count-2));
    m_vertices.add(m_vertices.at(count-1));
}

void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen)
{
    const qreal *pts = path.points();
    const QPainterPath::ElementType *types = path.elements();
    int count = path.elementCount();
    if (count < 2)
        return;

    float realWidth = qpen_widthf(pen);
    if (realWidth == 0)
        realWidth = 1;

    m_width = realWidth / 2;

    bool cosmetic = pen.isCosmetic();
    if (cosmetic) {
        m_width = m_width * m_inv_scale;
    }

    m_join_style = qpen_joinStyle(pen);
    m_cap_style = qpen_capStyle(pen);
    m_vertices.reset();
    m_miter_limit = pen.miterLimit() * qpen_widthf(pen);

    // The curvyness is based on the notion that I originally wanted
    // roughly one line segment pr 4 pixels. This may seem little, but
    // because we sample at constantly incrementing B(t) E [0<t<1], we
    // will get longer segments where the curvature is small and smaller
    // segments when the curvature is high.
    //
    // To get a rough idea of the length of each curve, I pretend that
    // the curve is a 90 degree arc, whose radius is
    // qMax(curveBounds.width, curveBounds.height). Based on this
    // logic we can estimate the length of the outline edges based on
    // the radius + a pen width and adjusting for scale factors
    // depending on if the pen is cosmetic or not.
    //
    // The curvyness value of PI/14 was based on,
    // arcLength=2*PI*r/4=PI/2 and splitting length into somewhere
    // between 3 and 8 where 5 seemed to be give pretty good results
    // hence: Q_PI/14. Lower divisors will give more detail at the
    // direct cost of performance.

    // simplfy pens that are thin in device size (2px wide or less)
    if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) {
        if (m_cap_style == Qt::RoundCap)
            m_cap_style = Qt::SquareCap;
        if (m_join_style == Qt::RoundJoin)
            m_join_style = Qt::MiterJoin;
        m_curvyness_add = 0.5;
        m_curvyness_mul = CURVE_FLATNESS / m_inv_scale;
        m_roundness = 1;
    } else if (cosmetic) {
        m_curvyness_add = realWidth / 2;
        m_curvyness_mul = CURVE_FLATNESS;
        m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS);
    } else {
        m_curvyness_add = m_width;
        m_curvyness_mul = CURVE_FLATNESS / m_inv_scale;
        m_roundness = qMax<int>(4, realWidth * m_curvyness_mul);
    }

    // Over this level of segmentation, there doesn't seem to be any
    // benefit, even for huge penWidth
    if (m_roundness > 24)
        m_roundness = 24;

    m_sin_theta = qFastSin(Q_PI / m_roundness);
    m_cos_theta = qFastCos(Q_PI / m_roundness);

    const qreal *endPts = pts + (count<<1);
    const qreal *startPts;

    Qt::PenCapStyle cap = m_cap_style;

    if (!types) {
        startPts = pts;

        bool endsAtStart = startPts[0] == *(endPts-2) && startPts[1] == *(endPts-1);

        Qt::PenCapStyle cap = m_cap_style;
        if (endsAtStart || path.hasImplicitClose())
            m_cap_style = Qt::FlatCap;
        moveTo(pts);
        m_cap_style = cap;
        pts += 2;
        lineTo(pts);
        pts += 2;
        while (pts < endPts) {
            join(pts);
            lineTo(pts);
            pts += 2;
        }

        endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);

    } else {
        bool endsAtStart;
        while (pts < endPts) {
            switch (*types) {
            case QPainterPath::MoveToElement: {
                if (pts != path.points())
                    endCapOrJoinClosed(startPts, pts, path.hasImplicitClose(), endsAtStart);

                startPts = pts;
                int end = (endPts - pts) / 2;
                int i = 2; // Start looking to ahead since we never have two moveto's in a row
                while (i<end && types[i] != QPainterPath::MoveToElement) {
                    ++i;
                }
                endsAtStart = startPts[0] == pts[i*2 - 2] && startPts[1] == pts[i*2 - 1];
                if (endsAtStart || path.hasImplicitClose())
                    m_cap_style = Qt::FlatCap;

                moveTo(pts);
                m_cap_style = cap;
                pts+=2;
                ++types;
                break; }
            case QPainterPath::LineToElement:
                if (*(types - 1) != QPainterPath::MoveToElement)
                    join(pts);
                lineTo(pts);
                pts+=2;
                ++types;
                break;
            case QPainterPath::CurveToElement:
                if (*(types - 1) != QPainterPath::MoveToElement)
                    join(pts);
                cubicTo(pts);
                pts+=6;
                types+=3;
                break;
            default:
                Q_ASSERT(false);
                break;
            }
        }

        endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
    }
}

void QTriangulatingStroker::cubicTo(const qreal *pts)
{
    const QPointF *p = (const QPointF *) pts;
    QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]);

    QRectF bounds = bezier.bounds();
    float rad = qMax(bounds.width(), bounds.height());
    int threshold = qMin<float>(64, (rad + m_curvyness_add) * m_curvyness_mul);
    if (threshold < 4)
        threshold = 4;
    qreal threshold_minus_1 = threshold - 1;
    float vx, vy;

    float cx = m_cx, cy = m_cy;
    float x, y;

    for (int i=1; i<threshold; ++i) {
        qreal t = qreal(i) / threshold_minus_1;
        QPointF p = bezier.pointAt(t);
        x = p.x();
        y = p.y();

        normalVector(cx, cy, x, y, &vx, &vy);

        emitLineSegment(x, y, vx, vy);

        cx = x;
        cy = y;
    }

    m_cx = cx;
    m_cy = cy;

    m_nvx = vx;
    m_nvy = vy;
}

static void qdashprocessor_moveTo(qreal x, qreal y, void *data)
{
    ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y);
}

static void qdashprocessor_lineTo(qreal x, qreal y, void *data)
{
    ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y);
}

static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, void *)
{
    Q_ASSERT(0); // The dasher should not produce curves...
}

QDashedStrokeProcessor::QDashedStrokeProcessor()
    : m_dash_stroker(0), m_inv_scale(1)
{
    m_dash_stroker.setMoveToHook(qdashprocessor_moveTo);
    m_dash_stroker.setLineToHook(qdashprocessor_lineTo);
    m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo);
}

void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen)
{

    const qreal *pts = path.points();
    const QPainterPath::ElementType *types = path.elements();
    int count = path.elementCount();

    m_points.reset();
    m_types.reset();

    qreal width = pen.width();
    if (width == 0)
        width = 1;

    m_dash_stroker.setDashPattern(pen.dashPattern());
    m_dash_stroker.setStrokeWidth(width);
    m_dash_stroker.setMiterLimit(pen.miterLimit());
    qreal curvyness = sqrt(width) * m_inv_scale / 8;

    if (count < 2)
        return;

    const qreal *endPts = pts + (count<<1);

    m_dash_stroker.begin(this);

    if (!types) {
        m_dash_stroker.moveTo(pts[0], pts[1]);
        pts += 2;
        while (pts < endPts) {
            m_dash_stroker.lineTo(pts[0], pts[1]);
            pts += 2;
        }
    } else {
        while (pts < endPts) {
            switch (*types) {
            case QPainterPath::MoveToElement:
                m_dash_stroker.moveTo(pts[0], pts[1]);
                pts += 2;
                ++types;
                break;
            case QPainterPath::LineToElement:
                m_dash_stroker.lineTo(pts[0], pts[1]);
                pts += 2;
                ++types;
                break;
            case QPainterPath::CurveToElement: {
                QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1),
                                                *(((const QPointF *) pts)),
                                                *(((const QPointF *) pts) + 1),
                                                *(((const QPointF *) pts) + 2));
                QRectF bounds = b.bounds();
                int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * curvyness);
                if (threshold < 4)
                    threshold = 4;
                qreal threshold_minus_1 = threshold - 1;
                for (int i=0; i<threshold; ++i) {
                    QPointF pt = b.pointAt(i / threshold_minus_1);
                    m_dash_stroker.lineTo(pt.x(), pt.y());
                }
                pts += 6;
                types += 3;
                break; }
            default: break;
            }
        }
    }

    m_dash_stroker.end();
}

QT_END_NAMESPACE

<commit_msg>Fixed stroking of cosmetic dashed pens with the GL2 paint engine.<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtOpenGL module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qtriangulatingstroker_p.h"
#include <qmath.h>

QT_BEGIN_NAMESPACE

#define CURVE_FLATNESS Q_PI / 8

void QTriangulatingStroker::endCapOrJoinClosed(const qreal *start, const qreal *cur,
                                               bool implicitClose, bool endsAtStart)
{
    if (endsAtStart) {
        join(start + 2);
    } else if (implicitClose) {
        join(start);
        lineTo(start);
        join(start+2);
    } else {
        endCap(cur);
    }
    int count = m_vertices.size();
    m_vertices.add(m_vertices.at(count-2));
    m_vertices.add(m_vertices.at(count-1));
}

void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen)
{
    const qreal *pts = path.points();
    const QPainterPath::ElementType *types = path.elements();
    int count = path.elementCount();
    if (count < 2)
        return;

    float realWidth = qpen_widthf(pen);
    if (realWidth == 0)
        realWidth = 1;

    m_width = realWidth / 2;

    bool cosmetic = pen.isCosmetic();
    if (cosmetic) {
        m_width = m_width * m_inv_scale;
    }

    m_join_style = qpen_joinStyle(pen);
    m_cap_style = qpen_capStyle(pen);
    m_vertices.reset();
    m_miter_limit = pen.miterLimit() * qpen_widthf(pen);

    // The curvyness is based on the notion that I originally wanted
    // roughly one line segment pr 4 pixels. This may seem little, but
    // because we sample at constantly incrementing B(t) E [0<t<1], we
    // will get longer segments where the curvature is small and smaller
    // segments when the curvature is high.
    //
    // To get a rough idea of the length of each curve, I pretend that
    // the curve is a 90 degree arc, whose radius is
    // qMax(curveBounds.width, curveBounds.height). Based on this
    // logic we can estimate the length of the outline edges based on
    // the radius + a pen width and adjusting for scale factors
    // depending on if the pen is cosmetic or not.
    //
    // The curvyness value of PI/14 was based on,
    // arcLength=2*PI*r/4=PI/2 and splitting length into somewhere
    // between 3 and 8 where 5 seemed to be give pretty good results
    // hence: Q_PI/14. Lower divisors will give more detail at the
    // direct cost of performance.

    // simplfy pens that are thin in device size (2px wide or less)
    if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) {
        if (m_cap_style == Qt::RoundCap)
            m_cap_style = Qt::SquareCap;
        if (m_join_style == Qt::RoundJoin)
            m_join_style = Qt::MiterJoin;
        m_curvyness_add = 0.5;
        m_curvyness_mul = CURVE_FLATNESS / m_inv_scale;
        m_roundness = 1;
    } else if (cosmetic) {
        m_curvyness_add = realWidth / 2;
        m_curvyness_mul = CURVE_FLATNESS;
        m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS);
    } else {
        m_curvyness_add = m_width;
        m_curvyness_mul = CURVE_FLATNESS / m_inv_scale;
        m_roundness = qMax<int>(4, realWidth * m_curvyness_mul);
    }

    // Over this level of segmentation, there doesn't seem to be any
    // benefit, even for huge penWidth
    if (m_roundness > 24)
        m_roundness = 24;

    m_sin_theta = qFastSin(Q_PI / m_roundness);
    m_cos_theta = qFastCos(Q_PI / m_roundness);

    const qreal *endPts = pts + (count<<1);
    const qreal *startPts;

    Qt::PenCapStyle cap = m_cap_style;

    if (!types) {
        startPts = pts;

        bool endsAtStart = startPts[0] == *(endPts-2) && startPts[1] == *(endPts-1);

        if (endsAtStart || path.hasImplicitClose())
            m_cap_style = Qt::FlatCap;
        moveTo(pts);
        m_cap_style = cap;
        pts += 2;
        lineTo(pts);
        pts += 2;
        while (pts < endPts) {
            join(pts);
            lineTo(pts);
            pts += 2;
        }

        endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);

    } else {
        bool endsAtStart;
        while (pts < endPts) {
            switch (*types) {
            case QPainterPath::MoveToElement: {
                if (pts != path.points())
                    endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);

                startPts = pts;
                int end = (endPts - pts) / 2;
                int i = 2; // Start looking to ahead since we never have two moveto's in a row
                while (i<end && types[i] != QPainterPath::MoveToElement) {
                    ++i;
                }
                endsAtStart = startPts[0] == pts[i*2 - 2] && startPts[1] == pts[i*2 - 1];
                if (endsAtStart || path.hasImplicitClose())
                    m_cap_style = Qt::FlatCap;

                moveTo(pts);
                m_cap_style = cap;
                pts+=2;
                ++types;
                break; }
            case QPainterPath::LineToElement:
                if (*(types - 1) != QPainterPath::MoveToElement)
                    join(pts);
                lineTo(pts);
                pts+=2;
                ++types;
                break;
            case QPainterPath::CurveToElement:
                if (*(types - 1) != QPainterPath::MoveToElement)
                    join(pts);
                cubicTo(pts);
                pts+=6;
                types+=3;
                break;
            default:
                Q_ASSERT(false);
                break;
            }
        }

        endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
    }
}

void QTriangulatingStroker::cubicTo(const qreal *pts)
{
    const QPointF *p = (const QPointF *) pts;
    QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]);

    QRectF bounds = bezier.bounds();
    float rad = qMax(bounds.width(), bounds.height());
    int threshold = qMin<float>(64, (rad + m_curvyness_add) * m_curvyness_mul);
    if (threshold < 4)
        threshold = 4;
    qreal threshold_minus_1 = threshold - 1;
    float vx, vy;

    float cx = m_cx, cy = m_cy;
    float x, y;

    for (int i=1; i<threshold; ++i) {
        qreal t = qreal(i) / threshold_minus_1;
        QPointF p = bezier.pointAt(t);
        x = p.x();
        y = p.y();

        normalVector(cx, cy, x, y, &vx, &vy);

        emitLineSegment(x, y, vx, vy);

        cx = x;
        cy = y;
    }

    m_cx = cx;
    m_cy = cy;

    m_nvx = vx;
    m_nvy = vy;
}

static void qdashprocessor_moveTo(qreal x, qreal y, void *data)
{
    ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y);
}

static void qdashprocessor_lineTo(qreal x, qreal y, void *data)
{
    ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y);
}

static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, void *)
{
    Q_ASSERT(0); // The dasher should not produce curves...
}

QDashedStrokeProcessor::QDashedStrokeProcessor()
    : m_dash_stroker(0), m_inv_scale(1)
{
    m_dash_stroker.setMoveToHook(qdashprocessor_moveTo);
    m_dash_stroker.setLineToHook(qdashprocessor_lineTo);
    m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo);
}

void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen)
{

    const qreal *pts = path.points();
    const QPainterPath::ElementType *types = path.elements();
    int count = path.elementCount();

    m_points.reset();
    m_types.reset();

    qreal width = qpen_widthf(pen);
    if (width == 0)
        width = 1;

    m_dash_stroker.setDashPattern(pen.dashPattern());
    m_dash_stroker.setStrokeWidth(pen.isCosmetic() ? width * m_inv_scale : width);
    m_dash_stroker.setMiterLimit(pen.miterLimit());
    qreal curvyness = sqrt(width) * m_inv_scale / 8;

    if (count < 2)
        return;

    const qreal *endPts = pts + (count<<1);

    m_dash_stroker.begin(this);

    if (!types) {
        m_dash_stroker.moveTo(pts[0], pts[1]);
        pts += 2;
        while (pts < endPts) {
            m_dash_stroker.lineTo(pts[0], pts[1]);
            pts += 2;
        }
    } else {
        while (pts < endPts) {
            switch (*types) {
            case QPainterPath::MoveToElement:
                m_dash_stroker.moveTo(pts[0], pts[1]);
                pts += 2;
                ++types;
                break;
            case QPainterPath::LineToElement:
                m_dash_stroker.lineTo(pts[0], pts[1]);
                pts += 2;
                ++types;
                break;
            case QPainterPath::CurveToElement: {
                QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1),
                                                *(((const QPointF *) pts)),
                                                *(((const QPointF *) pts) + 1),
                                                *(((const QPointF *) pts) + 2));
                QRectF bounds = b.bounds();
                int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * curvyness);
                if (threshold < 4)
                    threshold = 4;
                qreal threshold_minus_1 = threshold - 1;
                for (int i=0; i<threshold; ++i) {
                    QPointF pt = b.pointAt(i / threshold_minus_1);
                    m_dash_stroker.lineTo(pt.x(), pt.y());
                }
                pts += 6;
                types += 3;
                break; }
            default: break;
            }
        }
    }

    m_dash_stroker.end();
}

QT_END_NAMESPACE

<|endoftext|>


<commit_before>#pragma once

#include "WebUpdates.hpp"
#include "ClientWrapper.hpp"
#include "html5viewer/html5viewer.h"

#include <QMainWindow>
#include <QSettings>
#include <QMenu>
#include <QTimer>
#include <QUuid>

#include <QtWidgets>
//#include <QtWebEngineWidgets>

QT_BEGIN_NAMESPACE
class QWebEngineView;
class QLineEdit;
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT
    QSettings _settings;
    QMenu* _fileMenu;
    QMenu* _accountMenu;
    QString _deferredUrl;

    //Temporary storage for a web update description being considered for application.
    //Do not trust this as the in-use web update.
    WebUpdateManifest::UpdateDetails _webUpdateDescription;
    //Version information for the running client and GUI. These values may be trusted
    //as accurate regarding the web update currently being shown to the user.
    uint8_t _majorVersion = 0;
    uint8_t _forkVersion = 0;
    uint8_t _minorVersion = 0;
    uint8_t _patchVersion = 0;

    QTimer* _updateChecker;
    QUuid app_id;
    QString version;

  public:
    MainWindow();
    QMenu* fileMenu() { return _fileMenu; }
    QMenu* accountMenu() { return _accountMenu; }

    bool eventFilter(QObject* object, QEvent* event);

    ClientWrapper *clientWrapper() const;
    void setClientWrapper(ClientWrapper* clientWrapper);
    void navigateTo(const QString& path);

    bool detectCrash();
    QUuid getAppId() const { return app_id; }

public Q_SLOTS:
    void goToMyAccounts();
    void goToAccount(QString accountName);
    void goToCreateAccount();
    void goToAddContact();
    void checkWebUpdates(bool showNoUpdatesAlert = true,
                         std::function<void()> finishedCheckCallback = std::function<void()>());
    void loadWebUpdates();

    //Causes this window to attempt to become the front window on the desktop
    void takeFocus();
    void hideWindow();

    ///Used to schedule a custom URL for processing later, once the app has finished starting
    void deferCustomUrl(QString url);
    ///Triggers the deferred URL processign. Call once app has finished starting
    void processDeferredUrl();
    ///Used to process a custom URL now (only call if app has finished starting)
    void processCustomUrl(QString url);
    void goToBlock(uint32_t blockNumber);
    void goToBlock(QString blockId);
    void goToTransaction(QString transactionId);
    void importWallet();

private Q_SLOTS:
    void removeWebUpdates();

private:
    ClientWrapper* _clientWrapper;

    Html5Viewer* getViewer();
    bool walletIsUnlocked(bool promptToUnlock = true);
    std::string getLoginUser(const fc::ecc::public_key& serverKey);
    void doLogin(QStringList components);
    void goToTransfer(QStringList components);
    void readSettings();
    virtual void closeEvent( QCloseEvent* );
    void initMenu();
    void showNoUpdateAlert(QString info = tr(""));
    bool verifyUpdateSignature(QByteArray updatePackage);
    void goToRefCode(QStringList components);
};
<commit_msg>revert unexpected deleted codes<commit_after>#pragma once

#include "WebUpdates.hpp"
#include "ClientWrapper.hpp"
#include "html5viewer/html5viewer.h"

#include <QMainWindow>
#include <QSettings>
#include <QMenu>
#include <QTimer>
#include <QUuid>

#include <QtWidgets>
//#include <QtWebEngineWidgets>

QT_BEGIN_NAMESPACE
class QWebEngineView;
class QLineEdit;
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT
    QSettings _settings;
    QMenu* _fileMenu;
    QMenu* _accountMenu;
    QString _deferredUrl;

    QLineEdit *_locationEdit;
    QToolBar *_navToolBar;

    //Temporary storage for a web update description being considered for application.
    //Do not trust this as the in-use web update.
    WebUpdateManifest::UpdateDetails _webUpdateDescription;
    //Version information for the running client and GUI. These values may be trusted
    //as accurate regarding the web update currently being shown to the user.
    uint8_t _majorVersion = 0;
    uint8_t _forkVersion = 0;
    uint8_t _minorVersion = 0;
    uint8_t _patchVersion = 0;

    QTimer* _updateChecker;
    QUuid app_id;
    QString version;

  public:
    MainWindow();
    QMenu* fileMenu() { return _fileMenu; }
    QMenu* accountMenu() { return _accountMenu; }

    bool eventFilter(QObject* object, QEvent* event);

    ClientWrapper *clientWrapper() const;
    void setClientWrapper(ClientWrapper* clientWrapper);
    void navigateTo(const QString& path);

    bool detectCrash();
    QUuid getAppId() const { return app_id; }

public Q_SLOTS:
    void goToMyAccounts();
    void goToAccount(QString accountName);
    void goToCreateAccount();
    void goToAddContact();
    void checkWebUpdates(bool showNoUpdatesAlert = true,
                         std::function<void()> finishedCheckCallback = std::function<void()>());
    void loadWebUpdates();

    //Causes this window to attempt to become the front window on the desktop
    void takeFocus();
    void hideWindow();

    void setupNavToolbar();
    void changeLocation();
    void updateLocationEdit(const QUrl& newUrl);

    ///Used to schedule a custom URL for processing later, once the app has finished starting
    void deferCustomUrl(QString url);
    ///Triggers the deferred URL processign. Call once app has finished starting
    void processDeferredUrl();
    ///Used to process a custom URL now (only call if app has finished starting)
    void processCustomUrl(QString url);
    void goToBlock(uint32_t blockNumber);
    void goToBlock(QString blockId);
    void goToTransaction(QString transactionId);
    void importWallet();

private Q_SLOTS:
    void removeWebUpdates();

private:
    ClientWrapper* _clientWrapper;

    Html5Viewer* getViewer();
    bool walletIsUnlocked(bool promptToUnlock = true);
    std::string getLoginUser(const fc::ecc::public_key& serverKey);
    void doLogin(QStringList components);
    void goToTransfer(QStringList components);
    void readSettings();
    virtual void closeEvent( QCloseEvent* );
    void initMenu();
    void showNoUpdateAlert(QString info = tr(""));
    bool verifyUpdateSignature(QByteArray updatePackage);
    void goToRefCode(QStringList components);
};
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: xformsapi.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: obo $ $Date: 2004-11-16 10:15:57 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "xformsapi.hxx"

#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/xforms/XFormsSupplier.hpp>
#include <com/sun/star/xforms/XDataTypeRepository.hpp>
#include <com/sun/star/xforms/XModel.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/xsd/DataTypeClass.hpp>

#include <unotools/processfactory.hxx>
#include <tools/debug.hxx>

#include <xmltoken.hxx>
#include <nmspmap.hxx>
#include <xmlnmspe.hxx>
#include <xmltkmap.hxx>

using rtl::OUString;
using com::sun::star::uno::Reference;
using com::sun::star::uno::Sequence;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::uno::UNO_QUERY_THROW;
using com::sun::star::beans::XPropertySet;
using com::sun::star::container::XNameAccess;
using com::sun::star::lang::XMultiServiceFactory;
using com::sun::star::xforms::XFormsSupplier;
using com::sun::star::xforms::XDataTypeRepository;
using com::sun::star::container::XNameContainer;
using utl::getProcessServiceFactory;
using com::sun::star::uno::makeAny;
using com::sun::star::uno::Any;
using com::sun::star::uno::Exception;

using namespace com::sun::star;
using namespace xmloff::token;

Reference<XPropertySet> lcl_createPropertySet( const OUString& rServiceName )
{
    Reference<XMultiServiceFactory> xFactory = getProcessServiceFactory();
    DBG_ASSERT( xFactory.is(), "can't get service factory" );

    Reference<XPropertySet> xModel( xFactory->createInstance( rServiceName ),
                                    UNO_QUERY_THROW );
    DBG_ASSERT( xModel.is(), "can't create model" );

    return xModel;
}

Reference<XPropertySet> lcl_createXFormsModel()
{
    return lcl_createPropertySet( OUSTRING( "com.sun.star.xforms.Model" ) );
}

Reference<XPropertySet> lcl_createXFormsBinding()
{
    return lcl_createPropertySet( OUSTRING( "com.sun.star.xforms.Binding" ) );
}

void lcl_addXFormsModel(
    const Reference<frame::XModel>& xDocument,
    const Reference<XPropertySet>& xModel )
{
    bool bSuccess = false;
    try
    {
        Reference<XFormsSupplier> xSupplier( xDocument, UNO_QUERY );
        if( xSupplier.is() )
        {
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                OUString sName;
                xModel->getPropertyValue( OUSTRING("ID")) >>= sName;
                xForms->insertByName( sName, makeAny( xModel ) );
                bSuccess = true;
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    // TODO: implement proper error handling
    DBG_ASSERT( bSuccess, "can't import model" );
}

Reference<XPropertySet> lcl_findXFormsBindingOrSubmission(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID,
    bool bBinding )
{
    // find binding by iterating over all models, and look for the
    // given binding ID

    Reference<XPropertySet> xRet;
    try
    {
        // get supplier
        Reference<XFormsSupplier> xSupplier( xDocument, UNO_QUERY );
        if( xSupplier.is() )
        {
            // get XForms models
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                // iterate over all models
                Sequence<OUString> aNames = xForms->getElementNames();
                const OUString* pNames = aNames.getConstArray();
                sal_Int32 nNames = aNames.getLength();
                for( sal_Int32 n = 0; (n < nNames) && !xRet.is(); n++ )
                {
                    Reference<xforms::XModel> xModel(
                        xForms->getByName( pNames[n] ), UNO_QUERY );
                    if( xModel.is() )
                    {
                        // ask model for bindings
                        Reference<XNameAccess> xBindings(
                            bBinding
                                ? xModel->getBindings()
                                : xModel->getSubmissions(),
                            UNO_QUERY_THROW );

                        // finally, ask binding for name
                        if( xBindings->hasByName( rBindingID ) )
                            xRet.set( xBindings->getByName( rBindingID ),
                                      UNO_QUERY );
                    }
                }
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    if( ! xRet.is() )
        ; // TODO: rImport.SetError(...);

    return xRet;
}

Reference<XPropertySet> lcl_findXFormsBinding(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID )
{
    return lcl_findXFormsBindingOrSubmission( xDocument, rBindingID, true );
}

Reference<XPropertySet> lcl_findXFormsSubmission(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID )
{
    return lcl_findXFormsBindingOrSubmission( xDocument, rBindingID, false );
}

void lcl_setValue( Reference<XPropertySet>& xPropertySet,
                   const OUString& rName,
                   const Any rAny )
{
    xPropertySet->setPropertyValue( rName, rAny );
}

Reference<XPropertySet> lcl_getXFormsModel( const Reference<frame::XModel>& xDoc )
{
    Reference<XPropertySet> xRet;
    try
    {
        Reference<XFormsSupplier> xSupplier( xDoc, UNO_QUERY );
        if( xSupplier.is() )
        {
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                Sequence<OUString> aNames = xForms->getElementNames();
                if( aNames.getLength() > 0 )
                    xRet.set( xForms->getByName( aNames[0] ), UNO_QUERY );
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    return xRet;
}

#define TOKEN_MAP_ENTRY(NAMESPACE,TOKEN) { XML_NAMESPACE_##NAMESPACE, xmloff::token::XML_##TOKEN, xmloff::token::XML_##TOKEN }
static SvXMLTokenMapEntry aTypes[] =
{
    TOKEN_MAP_ENTRY( XSD, STRING  ),
    TOKEN_MAP_ENTRY( XSD, DECIMAL ),
    TOKEN_MAP_ENTRY( XSD, DOUBLE ),
    TOKEN_MAP_ENTRY( XSD, FLOAT ),
    TOKEN_MAP_ENTRY( XSD, BOOLEAN ),
    TOKEN_MAP_ENTRY( XSD, ANYURI ),
    TOKEN_MAP_ENTRY( XSD, DATETIME_XSD ),
    XML_TOKEN_MAP_END
};

rtl::OUString lcl_getTypeName(
    const Reference<XDataTypeRepository>& xRepository,
    const SvXMLNamespaceMap& rNamespaceMap,
    const OUString& rXMLName )
{
    // translate name into token for local name
    OUString sLocalName;
    sal_uInt16 nPrefix = rNamespaceMap.GetKeyByAttrName(rXMLName, &sLocalName);
    SvXMLTokenMap aMap( aTypes );
    sal_uInt16 mnToken = aMap.Get( nPrefix, sLocalName );

    OUString sTypeName = rXMLName;
    if( mnToken != XML_TOK_UNKNOWN )
    {
        // we found an XSD name: then get the proper API name for it
        DBG_ASSERT( xRepository.is(), "can't find type without repository");
        sal_uInt16 nTypeClass = com::sun::star::xsd::DataTypeClass::STRING;
        switch( mnToken )
        {
        case XML_STRING:
            nTypeClass = com::sun::star::xsd::DataTypeClass::STRING;
            break;
        case XML_ANYURI:
            nTypeClass = com::sun::star::xsd::DataTypeClass::anyURI;
            break;
        case XML_DECIMAL:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DECIMAL;
            break;
        case XML_DOUBLE:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DOUBLE;
            break;
        case XML_FLOAT:
            nTypeClass = com::sun::star::xsd::DataTypeClass::FLOAT;
            break;
        case XML_BOOLEAN:
            nTypeClass = com::sun::star::xsd::DataTypeClass::BOOLEAN;
            break;
        case XML_DATETIME_XSD:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DATETIME;
            break;

            /* data types not yet supported:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DURATION;
            nTypeClass = com::sun::star::xsd::DataTypeClass::TIME;
            nTypeClass = com::sun::star::xsd::DataTypeClass::DATE;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gYearMonth;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gYear;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gMonthDay;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gDay;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gMonth;
            nTypeClass = com::sun::star::xsd::DataTypeClass::hexBinary;
            nTypeClass = com::sun::star::xsd::DataTypeClass::base64Binary;
            nTypeClass = com::sun::star::xsd::DataTypeClass::QName;
            nTypeClass = com::sun::star::xsd::DataTypeClass::NOTATION;
            */
        }

        try
        {
            sTypeName = xRepository->getBasicDataType(nTypeClass)->getName();
        }
        catch( const Exception& )
        {
            DBG_ERROR( "exception during type creation" );
        }
    }
    return sTypeName;
}
<commit_msg>INTEGRATION: CWS eforms4 (1.2.22); FILE MERGED 2004/12/23 14:10:48 dvo 1.2.22.2: #i38666# load/save date+time related data types Issue number: Submitted by: Reviewed by: 2004/12/08 16:25:26 fs 1.2.22.1: #i36359# #i36303# care for DataTypeClass::DATE/TIME<commit_after>/*************************************************************************
 *
 *  $RCSfile: xformsapi.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: vg $ $Date: 2005-03-23 11:26:14 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include "xformsapi.hxx"

#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/xforms/XFormsSupplier.hpp>
#include <com/sun/star/xforms/XDataTypeRepository.hpp>
#include <com/sun/star/xforms/XModel.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/xsd/DataTypeClass.hpp>

#include <unotools/processfactory.hxx>
#include <tools/debug.hxx>

#include <xmltoken.hxx>
#include <nmspmap.hxx>
#include <xmlnmspe.hxx>
#include <xmltkmap.hxx>

using rtl::OUString;
using com::sun::star::uno::Reference;
using com::sun::star::uno::Sequence;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::uno::UNO_QUERY_THROW;
using com::sun::star::beans::XPropertySet;
using com::sun::star::container::XNameAccess;
using com::sun::star::lang::XMultiServiceFactory;
using com::sun::star::xforms::XFormsSupplier;
using com::sun::star::xforms::XDataTypeRepository;
using com::sun::star::container::XNameContainer;
using utl::getProcessServiceFactory;
using com::sun::star::uno::makeAny;
using com::sun::star::uno::Any;
using com::sun::star::uno::Exception;

using namespace com::sun::star;
using namespace xmloff::token;

Reference<XPropertySet> lcl_createPropertySet( const OUString& rServiceName )
{
    Reference<XMultiServiceFactory> xFactory = getProcessServiceFactory();
    DBG_ASSERT( xFactory.is(), "can't get service factory" );

    Reference<XPropertySet> xModel( xFactory->createInstance( rServiceName ),
                                    UNO_QUERY_THROW );
    DBG_ASSERT( xModel.is(), "can't create model" );

    return xModel;
}

Reference<XPropertySet> lcl_createXFormsModel()
{
    return lcl_createPropertySet( OUSTRING( "com.sun.star.xforms.Model" ) );
}

Reference<XPropertySet> lcl_createXFormsBinding()
{
    return lcl_createPropertySet( OUSTRING( "com.sun.star.xforms.Binding" ) );
}

void lcl_addXFormsModel(
    const Reference<frame::XModel>& xDocument,
    const Reference<XPropertySet>& xModel )
{
    bool bSuccess = false;
    try
    {
        Reference<XFormsSupplier> xSupplier( xDocument, UNO_QUERY );
        if( xSupplier.is() )
        {
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                OUString sName;
                xModel->getPropertyValue( OUSTRING("ID")) >>= sName;
                xForms->insertByName( sName, makeAny( xModel ) );
                bSuccess = true;
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    // TODO: implement proper error handling
    DBG_ASSERT( bSuccess, "can't import model" );
}

Reference<XPropertySet> lcl_findXFormsBindingOrSubmission(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID,
    bool bBinding )
{
    // find binding by iterating over all models, and look for the
    // given binding ID

    Reference<XPropertySet> xRet;
    try
    {
        // get supplier
        Reference<XFormsSupplier> xSupplier( xDocument, UNO_QUERY );
        if( xSupplier.is() )
        {
            // get XForms models
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                // iterate over all models
                Sequence<OUString> aNames = xForms->getElementNames();
                const OUString* pNames = aNames.getConstArray();
                sal_Int32 nNames = aNames.getLength();
                for( sal_Int32 n = 0; (n < nNames) && !xRet.is(); n++ )
                {
                    Reference<xforms::XModel> xModel(
                        xForms->getByName( pNames[n] ), UNO_QUERY );
                    if( xModel.is() )
                    {
                        // ask model for bindings
                        Reference<XNameAccess> xBindings(
                            bBinding
                                ? xModel->getBindings()
                                : xModel->getSubmissions(),
                            UNO_QUERY_THROW );

                        // finally, ask binding for name
                        if( xBindings->hasByName( rBindingID ) )
                            xRet.set( xBindings->getByName( rBindingID ),
                                      UNO_QUERY );
                    }
                }
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    if( ! xRet.is() )
        ; // TODO: rImport.SetError(...);

    return xRet;
}

Reference<XPropertySet> lcl_findXFormsBinding(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID )
{
    return lcl_findXFormsBindingOrSubmission( xDocument, rBindingID, true );
}

Reference<XPropertySet> lcl_findXFormsSubmission(
    Reference<frame::XModel>& xDocument,
    const rtl::OUString& rBindingID )
{
    return lcl_findXFormsBindingOrSubmission( xDocument, rBindingID, false );
}

void lcl_setValue( Reference<XPropertySet>& xPropertySet,
                   const OUString& rName,
                   const Any rAny )
{
    xPropertySet->setPropertyValue( rName, rAny );
}

Reference<XPropertySet> lcl_getXFormsModel( const Reference<frame::XModel>& xDoc )
{
    Reference<XPropertySet> xRet;
    try
    {
        Reference<XFormsSupplier> xSupplier( xDoc, UNO_QUERY );
        if( xSupplier.is() )
        {
            Reference<XNameContainer> xForms = xSupplier->getXForms();
            if( xForms.is() )
            {
                Sequence<OUString> aNames = xForms->getElementNames();
                if( aNames.getLength() > 0 )
                    xRet.set( xForms->getByName( aNames[0] ), UNO_QUERY );
            }
        }
    }
    catch( const Exception& )
    {
        ; // no success!
    }

    return xRet;
}

#define TOKEN_MAP_ENTRY(NAMESPACE,TOKEN) { XML_NAMESPACE_##NAMESPACE, xmloff::token::XML_##TOKEN, xmloff::token::XML_##TOKEN }
static SvXMLTokenMapEntry aTypes[] =
{
    TOKEN_MAP_ENTRY( XSD, STRING  ),
    TOKEN_MAP_ENTRY( XSD, DECIMAL ),
    TOKEN_MAP_ENTRY( XSD, DOUBLE ),
    TOKEN_MAP_ENTRY( XSD, FLOAT ),
    TOKEN_MAP_ENTRY( XSD, BOOLEAN ),
    TOKEN_MAP_ENTRY( XSD, ANYURI ),
    TOKEN_MAP_ENTRY( XSD, DATETIME_XSD ),
    TOKEN_MAP_ENTRY( XSD, DATE ),
    TOKEN_MAP_ENTRY( XSD, TIME ),
    TOKEN_MAP_ENTRY( XSD, YEAR ),
    TOKEN_MAP_ENTRY( XSD, MONTH ),
    TOKEN_MAP_ENTRY( XSD, DAY ),
    XML_TOKEN_MAP_END
};

sal_uInt16 lcl_getTypeClass(
    const Reference<XDataTypeRepository>& xRepository,
    const SvXMLNamespaceMap& rNamespaceMap,
    const OUString& rXMLName )
{
    // translate name into token for local name
    OUString sLocalName;
    sal_uInt16 nPrefix = rNamespaceMap.GetKeyByAttrName(rXMLName, &sLocalName);
    SvXMLTokenMap aMap( aTypes );
    sal_uInt16 mnToken = aMap.Get( nPrefix, sLocalName );

    sal_uInt16 nTypeClass = com::sun::star::xsd::DataTypeClass::STRING;
    if( mnToken != XML_TOK_UNKNOWN )
    {
        // we found an XSD name: then get the proper API name for it
        DBG_ASSERT( xRepository.is(), "can't find type without repository");
        switch( mnToken )
        {
        case XML_STRING:
            nTypeClass = com::sun::star::xsd::DataTypeClass::STRING;
            break;
        case XML_ANYURI:
            nTypeClass = com::sun::star::xsd::DataTypeClass::anyURI;
            break;
        case XML_DECIMAL:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DECIMAL;
            break;
        case XML_DOUBLE:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DOUBLE;
            break;
        case XML_FLOAT:
            nTypeClass = com::sun::star::xsd::DataTypeClass::FLOAT;
            break;
        case XML_BOOLEAN:
            nTypeClass = com::sun::star::xsd::DataTypeClass::BOOLEAN;
            break;
        case XML_DATETIME_XSD:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DATETIME;
            break;
        case XML_DATE:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DATE;
            break;
        case XML_TIME:
            nTypeClass = com::sun::star::xsd::DataTypeClass::TIME;
            break;
        case XML_YEAR:
            nTypeClass = com::sun::star::xsd::DataTypeClass::gYear;
            break;
        case XML_DAY:
            nTypeClass = com::sun::star::xsd::DataTypeClass::gDay;
            break;
        case XML_MONTH:
            nTypeClass = com::sun::star::xsd::DataTypeClass::gMonth;
            break;

            /* data types not yet supported:
            nTypeClass = com::sun::star::xsd::DataTypeClass::DURATION;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gYearMonth;
            nTypeClass = com::sun::star::xsd::DataTypeClass::gMonthDay;
            nTypeClass = com::sun::star::xsd::DataTypeClass::hexBinary;
            nTypeClass = com::sun::star::xsd::DataTypeClass::base64Binary;
            nTypeClass = com::sun::star::xsd::DataTypeClass::QName;
            nTypeClass = com::sun::star::xsd::DataTypeClass::NOTATION;
            */
        }
    }

    return nTypeClass;
}

rtl::OUString lcl_getTypeName(
    const Reference<XDataTypeRepository>& xRepository,
    const SvXMLNamespaceMap& rNamespaceMap,
    const OUString& rXMLName )
{
    OUString sLocalName;
    sal_uInt16 nPrefix = rNamespaceMap.GetKeyByAttrName(rXMLName, &sLocalName);
    SvXMLTokenMap aMap( aTypes );
    sal_uInt16 mnToken = aMap.Get( nPrefix, sLocalName );
    return ( mnToken == XML_TOK_UNKNOWN )
        ? rXMLName
        : lcl_getBasicTypeName( xRepository, rNamespaceMap, rXMLName );
}

rtl::OUString lcl_getBasicTypeName(
    const Reference<XDataTypeRepository>& xRepository,
    const SvXMLNamespaceMap& rNamespaceMap,
    const OUString& rXMLName )
{
    OUString sTypeName = rXMLName;
    try
    {
        sTypeName =
            xRepository->getBasicDataType(
                lcl_getTypeClass( xRepository, rNamespaceMap, rXMLName ) )
            ->getName();
    }
    catch( const Exception& )
    {
        DBG_ERROR( "exception during type creation" );
    }
    return sTypeName;
}
<|endoftext|>


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

#include "base/directory_watcher.h"

#include <errno.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <unistd.h>

#include <algorithm>
#include <set>
#include <utility>
#include <vector>

#include "base/eintr_wrapper.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/hash_tables.h"
#include "base/lock.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "base/singleton.h"
#include "base/task.h"
#include "base/thread.h"
#include "base/waitable_event.h"

namespace {

class DirectoryWatcherImpl;

// Singleton to manage all inotify watches.
class InotifyReader {
 public:
  typedef int Watch;  // Watch descriptor used by AddWatch and RemoveWatch.
  static const Watch kInvalidWatch = -1;

  // Watch |path| for changes. |watcher| will be notified on each change.
  // Returns kInvalidWatch on failure.
  Watch AddWatch(const FilePath& path, DirectoryWatcherImpl* watcher);

  // Remove |watch|. Returns true on success.
  bool RemoveWatch(Watch watch, DirectoryWatcherImpl* watcher);

  // Callback for InotifyReaderTask.
  void OnInotifyEvent(const inotify_event* event);

 private:
  friend struct DefaultSingletonTraits<InotifyReader>;

  typedef std::set<DirectoryWatcherImpl*> WatcherSet;

  InotifyReader();
  ~InotifyReader();

  // We keep track of which delegates want to be notified on which watches.
  base::hash_map<Watch, WatcherSet> watchers_;

  // For each watch we also want to know the path it's watching.
  base::hash_map<Watch, FilePath> paths_;

  // Lock to protect delegates_ and paths_.
  Lock lock_;

  // Separate thread on which we run blocking read for inotify events.
  base::Thread thread_;

  // File descriptor returned by inotify_init.
  const int inotify_fd_;

  // Use self-pipe trick to unblock select during shutdown.
  int shutdown_pipe_[2];

  // Flag set to true when startup was successful.
  bool valid_;

  DISALLOW_COPY_AND_ASSIGN(InotifyReader);
};

class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
 public:
  typedef std::set<FilePath> FilePathSet;

  DirectoryWatcherImpl();
  ~DirectoryWatcherImpl();

  void EnsureSetupFinished();

  // Called for each event coming from one of watches.
  void OnInotifyEvent(const inotify_event* event);

  // Callback for RegisterSubtreeWatchesTask.
  bool OnEnumeratedSubtree(const FilePathSet& paths);

  // Start watching |path| for changes and notify |delegate| on each change.
  // If |recursive| is true, watch entire subtree.
  // Returns true if watch for |path| has been added successfully. Watches
  // required for |recursive| are added on a background thread and have no
  // effect on the return value.
  virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
                     MessageLoop* backend_loop, bool recursive);

 private:
  typedef std::set<InotifyReader::Watch> WatchSet;
  typedef std::set<ino_t> InodeSet;

  // Returns true if |inode| is watched by DirectoryWatcherImpl.
  bool IsInodeWatched(ino_t inode) const;

  // Delegate to notify upon changes.
  DirectoryWatcher::Delegate* delegate_;

  // Path we're watching (passed to delegate).
  FilePath root_path_;

  // Watch returned by InotifyReader.
  InotifyReader::Watch watch_;

  // Set of watched inodes.
  InodeSet inodes_watched_;

  // Keep track of registered watches.
  WatchSet watches_;

  // Lock to protect inodes_watched_ and watches_.
  Lock lock_;

  // Flag set to true when recursively watching subtree.
  bool recursive_;

  // Loop where we post directory change notifications to.
  MessageLoop* loop_;

  // Event signaled when the background task finished adding initial inotify
  // watches for recursive watch.
  base::WaitableEvent recursive_setup_finished_;

  DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl);
};

class RegisterSubtreeWatchesTask : public Task {
 public:
  RegisterSubtreeWatchesTask(DirectoryWatcherImpl* watcher,
                             const FilePath& path)
      : watcher_(watcher),
        path_(path) {
  }

  virtual void Run() {
    file_util::FileEnumerator dir_list(path_, true,
        file_util::FileEnumerator::DIRECTORIES);

    DirectoryWatcherImpl::FilePathSet subtree;
    for (FilePath subdirectory = dir_list.Next();
         !subdirectory.empty();
         subdirectory = dir_list.Next()) {
      subtree.insert(subdirectory);
    }
    watcher_->OnEnumeratedSubtree(subtree);
  }

 private:
  DirectoryWatcherImpl* watcher_;
  FilePath path_;

  DISALLOW_COPY_AND_ASSIGN(RegisterSubtreeWatchesTask);
};

class DirectoryWatcherImplNotifyTask : public Task {
 public:
  DirectoryWatcherImplNotifyTask(DirectoryWatcher::Delegate* delegate,
                                 const FilePath& path)
      : delegate_(delegate),
        path_(path) {
  }

  virtual void Run() {
    delegate_->OnDirectoryChanged(path_);
  }

 private:
  DirectoryWatcher::Delegate* delegate_;
  FilePath path_;

  DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImplNotifyTask);
};

class InotifyReaderTask : public Task {
 public:
  InotifyReaderTask(InotifyReader* reader, int inotify_fd, int shutdown_fd)
      : reader_(reader),
        inotify_fd_(inotify_fd),
        shutdown_fd_(shutdown_fd) {
  }

  virtual void Run() {
    while (true) {
      fd_set rfds;
      FD_ZERO(&rfds);
      FD_SET(inotify_fd_, &rfds);
      FD_SET(shutdown_fd_, &rfds);

      // Wait until some inotify events are available.
      int select_result =
        HANDLE_EINTR(select(std::max(inotify_fd_, shutdown_fd_) + 1,
                            &rfds, NULL, NULL, NULL));
      if (select_result < 0) {
        DLOG(WARNING) << "select failed: " << strerror(errno);
        return;
      }

      if (FD_ISSET(shutdown_fd_, &rfds))
        return;

      // Adjust buffer size to current event queue size.
      int buffer_size;
      int ioctl_result = HANDLE_EINTR(ioctl(inotify_fd_, FIONREAD,
                                            &buffer_size));

      if (ioctl_result != 0) {
        DLOG(WARNING) << "ioctl failed: " << strerror(errno);
        return;
      }

      std::vector<char> buffer(buffer_size);

      ssize_t bytes_read = HANDLE_EINTR(read(inotify_fd_, &buffer[0],
                                             buffer_size));

      if (bytes_read < 0) {
        DLOG(WARNING) << "read from inotify fd failed: " << strerror(errno);
        return;
      }

      ssize_t i = 0;
      while (i < bytes_read) {
        inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]);
        size_t event_size = sizeof(inotify_event) + event->len;
        DCHECK(i + event_size <= static_cast<size_t>(bytes_read));
        reader_->OnInotifyEvent(event);
        i += event_size;
      }
    }
  }

 private:
  InotifyReader* reader_;
  int inotify_fd_;
  int shutdown_fd_;

  DISALLOW_COPY_AND_ASSIGN(InotifyReaderTask);
};

InotifyReader::InotifyReader()
    : thread_("inotify_reader"),
      inotify_fd_(inotify_init()),
      valid_(false) {
  shutdown_pipe_[0] = -1;
  shutdown_pipe_[1] = -1;
  if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) {
    thread_.message_loop()->PostTask(
        FROM_HERE, new InotifyReaderTask(this, inotify_fd_, shutdown_pipe_[0]));
    valid_ = true;
  }
}

InotifyReader::~InotifyReader() {
  if (valid_) {
    // Write to the self-pipe so that the select call in InotifyReaderTask
    // returns.
    HANDLE_EINTR(write(shutdown_pipe_[1], "", 1));
    thread_.Stop();
  }
  if (inotify_fd_ >= 0)
    close(inotify_fd_);
  if (shutdown_pipe_[0] >= 0)
    close(shutdown_pipe_[0]);
  if (shutdown_pipe_[1] >= 0)
    close(shutdown_pipe_[1]);
}

InotifyReader::Watch InotifyReader::AddWatch(
    const FilePath& path, DirectoryWatcherImpl* watcher) {

  if (!valid_)
    return kInvalidWatch;

  AutoLock auto_lock(lock_);

  Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(),
                                  IN_CREATE | IN_DELETE |
                                  IN_CLOSE_WRITE | IN_MOVE);

  if (watch == kInvalidWatch)
    return kInvalidWatch;

  if (paths_[watch].empty())
    paths_[watch] = path;  // We don't yet watch this path.

  watchers_[watch].insert(watcher);

  return watch;
}

bool InotifyReader::RemoveWatch(Watch watch,
                                DirectoryWatcherImpl* watcher) {
  if (!valid_)
    return false;

  AutoLock auto_lock(lock_);

  if (paths_[watch].empty())
    return false;  // We don't recognize this watch.

  watchers_[watch].erase(watcher);

  if (watchers_[watch].empty()) {
    paths_.erase(watch);
    watchers_.erase(watch);
    return (inotify_rm_watch(inotify_fd_, watch) == 0);
  }

  return true;
}

void InotifyReader::OnInotifyEvent(const inotify_event* event) {
  if (event->mask & IN_IGNORED)
    return;

  WatcherSet watchers_to_notify;
  FilePath changed_path;

  {
    AutoLock auto_lock(lock_);
    changed_path = paths_[event->wd];
    watchers_to_notify.insert(watchers_[event->wd].begin(),
                              watchers_[event->wd].end());
  }

  for (WatcherSet::iterator watcher = watchers_to_notify.begin();
       watcher != watchers_to_notify.end();
       ++watcher) {
    (*watcher)->OnInotifyEvent(event);
  }
}

DirectoryWatcherImpl::DirectoryWatcherImpl()
    : watch_(InotifyReader::kInvalidWatch),
      recursive_setup_finished_(false, false) {
}

DirectoryWatcherImpl::~DirectoryWatcherImpl() {
  if (watch_ == InotifyReader::kInvalidWatch)
    return;

  if (recursive_)
    recursive_setup_finished_.Wait();
  for (WatchSet::iterator watch = watches_.begin();
       watch != watches_.end();
       ++watch) {
    Singleton<InotifyReader>::get()->RemoveWatch(*watch, this);
  }
  watches_.clear();
  inodes_watched_.clear();
}

void DirectoryWatcherImpl::OnInotifyEvent(const inotify_event* event) {
  loop_->PostTask(FROM_HERE,
      new DirectoryWatcherImplNotifyTask(delegate_, root_path_));

  if (!(event->mask & IN_ISDIR))
    return;

  if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
    // TODO(phajdan.jr): add watch for this new directory.
    NOTIMPLEMENTED();
  } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
    // TODO(phajdan.jr): remove our watch for this directory.
    NOTIMPLEMENTED();
  }
}

bool DirectoryWatcherImpl::IsInodeWatched(ino_t inode) const {
  return inodes_watched_.find(inode) != inodes_watched_.end();
}

bool DirectoryWatcherImpl::OnEnumeratedSubtree(const FilePathSet& subtree) {
  DCHECK(recursive_);

  if (watch_ == InotifyReader::kInvalidWatch) {
    recursive_setup_finished_.Signal();
    return false;
  }

  bool success = true;

  {
    // Limit the scope of auto_lock so it releases lock_ before we signal
    // recursive_setup_finished_. Our dtor waits on recursive_setup_finished_
    // and could otherwise destroy the lock before we release it.
    AutoLock auto_lock(lock_);

    for (FilePathSet::iterator subdirectory = subtree.begin();
         subdirectory != subtree.end();
         ++subdirectory) {
      ino_t inode;
      if (!file_util::GetInode(*subdirectory, &inode)) {
        success = false;
        continue;
      }
      if (IsInodeWatched(inode))
        continue;
      InotifyReader::Watch watch =
        Singleton<InotifyReader>::get()->AddWatch(*subdirectory, this);
      if (watch != InotifyReader::kInvalidWatch) {
        watches_.insert(watch);
        inodes_watched_.insert(inode);
      }
    }
  }

  recursive_setup_finished_.Signal();
  return success;
}

bool DirectoryWatcherImpl::Watch(const FilePath& path,
                                 DirectoryWatcher::Delegate* delegate,
                                 MessageLoop* backend_loop, bool recursive) {

  // Can only watch one path.
  DCHECK(watch_ == InotifyReader::kInvalidWatch);

  ino_t inode;
  if (!file_util::GetInode(path, &inode))
    return false;

  InotifyReader::Watch watch =
      Singleton<InotifyReader>::get()->AddWatch(path, this);
  if (watch == InotifyReader::kInvalidWatch)
    return false;

  delegate_ = delegate;
  recursive_ = recursive;
  root_path_ = path;
  watch_ = watch;
  loop_ = MessageLoop::current();

  {
    AutoLock auto_lock(lock_);
    inodes_watched_.insert(inode);
    watches_.insert(watch_);
  }

  if (recursive_) {
    Task* subtree_task = new RegisterSubtreeWatchesTask(this, root_path_);
    if (backend_loop) {
      backend_loop->PostTask(FROM_HERE, subtree_task);
    } else {
      subtree_task->Run();
      delete subtree_task;
    }
  }

  return true;
}

}  // namespace

DirectoryWatcher::DirectoryWatcher() {
  impl_ = new DirectoryWatcherImpl();
}

<commit_msg>Fix two races in DirectoryWatcherInotify:<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/directory_watcher.h"

#include <errno.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <unistd.h>

#include <algorithm>
#include <set>
#include <utility>
#include <vector>

#include "base/eintr_wrapper.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/hash_tables.h"
#include "base/lock.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "base/singleton.h"
#include "base/task.h"
#include "base/thread.h"
#include "base/waitable_event.h"

namespace {

class DirectoryWatcherImpl;

// Singleton to manage all inotify watches.
class InotifyReader {
 public:
  typedef int Watch;  // Watch descriptor used by AddWatch and RemoveWatch.
  static const Watch kInvalidWatch = -1;

  // Watch |path| for changes. |watcher| will be notified on each change.
  // Returns kInvalidWatch on failure.
  Watch AddWatch(const FilePath& path, DirectoryWatcherImpl* watcher);

  // Remove |watch|. Returns true on success.
  bool RemoveWatch(Watch watch, DirectoryWatcherImpl* watcher);

  // Callback for InotifyReaderTask.
  void OnInotifyEvent(const inotify_event* event);

 private:
  friend struct DefaultSingletonTraits<InotifyReader>;

  typedef std::set<DirectoryWatcherImpl*> WatcherSet;

  InotifyReader();
  ~InotifyReader();

  // We keep track of which delegates want to be notified on which watches.
  base::hash_map<Watch, WatcherSet> watchers_;

  // For each watch we also want to know the path it's watching.
  base::hash_map<Watch, FilePath> paths_;

  // Lock to protect delegates_ and paths_.
  Lock lock_;

  // Separate thread on which we run blocking read for inotify events.
  base::Thread thread_;

  // File descriptor returned by inotify_init.
  const int inotify_fd_;

  // Use self-pipe trick to unblock select during shutdown.
  int shutdown_pipe_[2];

  // Flag set to true when startup was successful.
  bool valid_;

  DISALLOW_COPY_AND_ASSIGN(InotifyReader);
};

class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
 public:
  typedef std::set<FilePath> FilePathSet;

  DirectoryWatcherImpl();
  ~DirectoryWatcherImpl();

  void EnsureSetupFinished();

  // Called for each event coming from one of watches.
  void OnInotifyEvent(const inotify_event* event);

  // Callback for RegisterSubtreeWatchesTask.
  bool OnEnumeratedSubtree(const FilePathSet& paths);

  // Start watching |path| for changes and notify |delegate| on each change.
  // If |recursive| is true, watch entire subtree.
  // Returns true if watch for |path| has been added successfully. Watches
  // required for |recursive| are added on a background thread and have no
  // effect on the return value.
  virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
                     MessageLoop* backend_loop, bool recursive);

 private:
  typedef std::set<InotifyReader::Watch> WatchSet;
  typedef std::set<ino_t> InodeSet;

  // Returns true if |inode| is watched by DirectoryWatcherImpl.
  bool IsInodeWatched(ino_t inode) const;

  // Delegate to notify upon changes.
  DirectoryWatcher::Delegate* delegate_;

  // Path we're watching (passed to delegate).
  FilePath root_path_;

  // Watch returned by InotifyReader.
  InotifyReader::Watch watch_;

  // Set of watched inodes.
  InodeSet inodes_watched_;

  // Keep track of registered watches.
  WatchSet watches_;

  // Lock to protect inodes_watched_ and watches_.
  Lock lock_;

  // Flag set to true when recursively watching subtree.
  bool recursive_;

  // Loop where we post directory change notifications to.
  MessageLoop* loop_;

  // Event signaled when the background task finished adding initial inotify
  // watches for recursive watch.
  base::WaitableEvent recursive_setup_finished_;

  DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl);
};

class RegisterSubtreeWatchesTask : public Task {
 public:
  RegisterSubtreeWatchesTask(DirectoryWatcherImpl* watcher,
                             const FilePath& path)
      : watcher_(watcher),
        path_(path) {
  }

  virtual void Run() {
    file_util::FileEnumerator dir_list(path_, true,
        file_util::FileEnumerator::DIRECTORIES);

    DirectoryWatcherImpl::FilePathSet subtree;
    for (FilePath subdirectory = dir_list.Next();
         !subdirectory.empty();
         subdirectory = dir_list.Next()) {
      subtree.insert(subdirectory);
    }
    watcher_->OnEnumeratedSubtree(subtree);
  }

 private:
  DirectoryWatcherImpl* watcher_;
  FilePath path_;

  DISALLOW_COPY_AND_ASSIGN(RegisterSubtreeWatchesTask);
};

class DirectoryWatcherImplNotifyTask : public Task {
 public:
  DirectoryWatcherImplNotifyTask(DirectoryWatcher::Delegate* delegate,
                                 const FilePath& path)
      : delegate_(delegate),
        path_(path) {
  }

  virtual void Run() {
    delegate_->OnDirectoryChanged(path_);
  }

 private:
  DirectoryWatcher::Delegate* delegate_;
  FilePath path_;

  DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImplNotifyTask);
};

class InotifyReaderTask : public Task {
 public:
  InotifyReaderTask(InotifyReader* reader, int inotify_fd, int shutdown_fd)
      : reader_(reader),
        inotify_fd_(inotify_fd),
        shutdown_fd_(shutdown_fd) {
  }

  virtual void Run() {
    while (true) {
      fd_set rfds;
      FD_ZERO(&rfds);
      FD_SET(inotify_fd_, &rfds);
      FD_SET(shutdown_fd_, &rfds);

      // Wait until some inotify events are available.
      int select_result =
        HANDLE_EINTR(select(std::max(inotify_fd_, shutdown_fd_) + 1,
                            &rfds, NULL, NULL, NULL));
      if (select_result < 0) {
        DLOG(WARNING) << "select failed: " << strerror(errno);
        return;
      }

      if (FD_ISSET(shutdown_fd_, &rfds))
        return;

      // Adjust buffer size to current event queue size.
      int buffer_size;
      int ioctl_result = HANDLE_EINTR(ioctl(inotify_fd_, FIONREAD,
                                            &buffer_size));

      if (ioctl_result != 0) {
        DLOG(WARNING) << "ioctl failed: " << strerror(errno);
        return;
      }

      std::vector<char> buffer(buffer_size);

      ssize_t bytes_read = HANDLE_EINTR(read(inotify_fd_, &buffer[0],
                                             buffer_size));

      if (bytes_read < 0) {
        DLOG(WARNING) << "read from inotify fd failed: " << strerror(errno);
        return;
      }

      ssize_t i = 0;
      while (i < bytes_read) {
        inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]);
        size_t event_size = sizeof(inotify_event) + event->len;
        DCHECK(i + event_size <= static_cast<size_t>(bytes_read));
        reader_->OnInotifyEvent(event);
        i += event_size;
      }
    }
  }

 private:
  InotifyReader* reader_;
  int inotify_fd_;
  int shutdown_fd_;

  DISALLOW_COPY_AND_ASSIGN(InotifyReaderTask);
};

InotifyReader::InotifyReader()
    : thread_("inotify_reader"),
      inotify_fd_(inotify_init()),
      valid_(false) {
  shutdown_pipe_[0] = -1;
  shutdown_pipe_[1] = -1;
  if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) {
    thread_.message_loop()->PostTask(
        FROM_HERE, new InotifyReaderTask(this, inotify_fd_, shutdown_pipe_[0]));
    valid_ = true;
  }
}

InotifyReader::~InotifyReader() {
  if (valid_) {
    // Write to the self-pipe so that the select call in InotifyReaderTask
    // returns.
    HANDLE_EINTR(write(shutdown_pipe_[1], "", 1));
    thread_.Stop();
  }
  if (inotify_fd_ >= 0)
    close(inotify_fd_);
  if (shutdown_pipe_[0] >= 0)
    close(shutdown_pipe_[0]);
  if (shutdown_pipe_[1] >= 0)
    close(shutdown_pipe_[1]);
}

InotifyReader::Watch InotifyReader::AddWatch(
    const FilePath& path, DirectoryWatcherImpl* watcher) {

  if (!valid_)
    return kInvalidWatch;

  AutoLock auto_lock(lock_);

  Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(),
                                  IN_CREATE | IN_DELETE |
                                  IN_CLOSE_WRITE | IN_MOVE);

  if (watch == kInvalidWatch)
    return kInvalidWatch;

  if (paths_[watch].empty())
    paths_[watch] = path;  // We don't yet watch this path.

  watchers_[watch].insert(watcher);

  return watch;
}

bool InotifyReader::RemoveWatch(Watch watch,
                                DirectoryWatcherImpl* watcher) {
  if (!valid_)
    return false;

  AutoLock auto_lock(lock_);

  if (paths_[watch].empty())
    return false;  // We don't recognize this watch.

  watchers_[watch].erase(watcher);

  if (watchers_[watch].empty()) {
    paths_.erase(watch);
    watchers_.erase(watch);
    return (inotify_rm_watch(inotify_fd_, watch) == 0);
  }

  return true;
}

void InotifyReader::OnInotifyEvent(const inotify_event* event) {
  if (event->mask & IN_IGNORED)
    return;

  // In case you want to limit the scope of this lock, it's not sufficient
  // to just copy things under the lock, and then run the notifications
  // without holding the lock. DirectoryWatcherImpl's dtor removes its watches,
  // and to do that obtains the lock. After it finishes removing watches,
  // it's destroyed. So, if you copy under the lock and notify without the lock,
  // it's possible you'll copy the DirectoryWatcherImpl which is being
  // destroyed, then it will destroy itself, and then you'll try to notify it.
  AutoLock auto_lock(lock_);

  for (WatcherSet::iterator watcher = watchers_[event->wd].begin();
       watcher != watchers_[event->wd].end();
       ++watcher) {
    (*watcher)->OnInotifyEvent(event);
  }
}

DirectoryWatcherImpl::DirectoryWatcherImpl()
    : watch_(InotifyReader::kInvalidWatch),
      recursive_setup_finished_(false, false) {
}

DirectoryWatcherImpl::~DirectoryWatcherImpl() {
  if (watch_ == InotifyReader::kInvalidWatch)
    return;

  if (recursive_)
    recursive_setup_finished_.Wait();
  for (WatchSet::iterator watch = watches_.begin();
       watch != watches_.end();
       ++watch) {
    Singleton<InotifyReader>::get()->RemoveWatch(*watch, this);
  }
  watches_.clear();
  inodes_watched_.clear();
}

void DirectoryWatcherImpl::OnInotifyEvent(const inotify_event* event) {
  loop_->PostTask(FROM_HERE,
      new DirectoryWatcherImplNotifyTask(delegate_, root_path_));

  if (!(event->mask & IN_ISDIR))
    return;

  if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
    // TODO(phajdan.jr): add watch for this new directory.
    NOTIMPLEMENTED();
  } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
    // TODO(phajdan.jr): remove our watch for this directory.
    NOTIMPLEMENTED();
  }
}

bool DirectoryWatcherImpl::IsInodeWatched(ino_t inode) const {
  return inodes_watched_.find(inode) != inodes_watched_.end();
}

bool DirectoryWatcherImpl::OnEnumeratedSubtree(const FilePathSet& subtree) {
  DCHECK(recursive_);

  if (watch_ == InotifyReader::kInvalidWatch) {
    recursive_setup_finished_.Signal();
    return false;
  }

  bool success = true;

  {
    // Limit the scope of auto_lock so it releases lock_ before we signal
    // recursive_setup_finished_. Our dtor waits on recursive_setup_finished_
    // and could otherwise destroy the lock before we release it.
    AutoLock auto_lock(lock_);

    for (FilePathSet::iterator subdirectory = subtree.begin();
         subdirectory != subtree.end();
         ++subdirectory) {
      ino_t inode;
      if (!file_util::GetInode(*subdirectory, &inode)) {
        success = false;
        continue;
      }
      if (IsInodeWatched(inode))
        continue;
      InotifyReader::Watch watch =
        Singleton<InotifyReader>::get()->AddWatch(*subdirectory, this);
      if (watch != InotifyReader::kInvalidWatch) {
        watches_.insert(watch);
        inodes_watched_.insert(inode);
      }
    }
  }

  recursive_setup_finished_.Signal();
  return success;
}

bool DirectoryWatcherImpl::Watch(const FilePath& path,
                                 DirectoryWatcher::Delegate* delegate,
                                 MessageLoop* backend_loop, bool recursive) {

  // Can only watch one path.
  DCHECK(watch_ == InotifyReader::kInvalidWatch);

  ino_t inode;
  if (!file_util::GetInode(path, &inode))
    return false;

  delegate_ = delegate;
  recursive_ = recursive;
  root_path_ = path;
  loop_ = MessageLoop::current();
  watch_ = Singleton<InotifyReader>::get()->AddWatch(path, this);
  if (watch_ == InotifyReader::kInvalidWatch)
    return false;

  {
    AutoLock auto_lock(lock_);
    inodes_watched_.insert(inode);
    watches_.insert(watch_);
  }

  if (recursive_) {
    Task* subtree_task = new RegisterSubtreeWatchesTask(this, root_path_);
    if (backend_loop) {
      backend_loop->PostTask(FROM_HERE, subtree_task);
    } else {
      subtree_task->Run();
      delete subtree_task;
    }
  }

  return true;
}

}  // namespace

DirectoryWatcher::DirectoryWatcher() {
  impl_ = new DirectoryWatcherImpl();
}
<|endoftext|>