<commit_before>#include <QtTest>
#include <QtWidgets>
#include <DO/Graphics/DerivedQObjects/PaintingWindow.hpp>

Q_DECLARE_METATYPE(DO::Event);

using namespace DO;

class TestPaintingWindowConstructors: public QObject
{
  Q_OBJECT

private slots:
  void test_construction_of_PaintingWindow_with_small_size()
  {
    int width = 50;
    int height = 50;
    QString windowName = "painting window";
    int x = 200;
    int y = 300;

    PaintingWindow *window = new PaintingWindow(width, height,
                                                windowName,
                                                x, y);

    QCOMPARE(window->width(), width);
    QCOMPARE(window->height(), height);
    QCOMPARE(window->windowTitle(), windowName);
#ifndef __APPLE__
    // Strangely, when we have 2 monitor screens, this fails on Mac OS X...
    QCOMPARE(window->x(), x);
    QCOMPARE(window->y(), y);
#endif
    QVERIFY(window->isVisible());

    window->deleteLater();
  }

  void test_construction_of_PaintingWindow_with_size_larger_than_desktop()
  {
    int width = qApp->desktop()->width();
    int height = qApp->desktop()->height();

    PaintingWindow *window = new PaintingWindow(width, height);

    QVERIFY(window->scrollArea()->isMaximized());
    QVERIFY(window->isVisible());

    window->deleteLater();
  }
};

class TestPaintingWindowDrawMethods: public QObject
{
  Q_OBJECT

private: // data members
  PaintingWindow *test_window_;
  QImage true_image_;
  QPainter painter_;

private: // methods
  QImage get_image_from_window()
  {
    QImage image(test_window_->size(), QImage::Format_RGB32);
    test_window_->render(&image, QPoint(), QRegion(QRect(QPoint(),
                         test_window_->size())));
    return image;
  }

private slots:
  void initTestCase()
  {
    test_window_ = new PaintingWindow(300, 300);
    true_image_ =  QImage(test_window_->size(), QImage::Format_RGB32);
  }

  void init()
  {
    test_window_->clear();
    true_image_.fill(Qt::white);
    painter_.begin(&true_image_);
  }

  void cleanup()
  {
    painter_.end();
  }

  void cleanupTestCase()
  {
    test_window_->deleteLater();
  }

  void test_drawPoint_using_integer_coordinates()
  {
    int x = 150, y = 100;
    QColor color(255, 0, 0);

    test_window_->drawPoint(x, y, color);

    painter_.setPen(color);
    painter_.drawPoint(QPoint(x, y));

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawPoint_using_QPointF()
  {
    double x = 150.95, y = 100.3333;
    QColor color(0, 225, 0);

    test_window_->drawPoint(QPointF(x, y), color);

    painter_.setPen(color);
    painter_.drawPoint(QPointF(x, y));

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawLine_using_integer_coordinates()
  {
    int x1 = 100, y1 = 100;
    int x2 = 200, y2 = 240;
    QColor color(0, 255, 0);
    int thickness = 3;

    test_window_->drawLine(x1, y1, x2, y2, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawLine(x1, y1, x2, y2);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawLine_using_QPointF()
  {
    QPointF p1(100.350, 100.699);
    QPointF p2(203.645, 240.664);
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawLine(p1, p2, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawLine(p1, p2);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawCircle_using_integer_coordinates()
  {
    int xc = 150, yc = 123;
    int r = 39;
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawCircle(xc, yc, r, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawEllipse(QPoint(xc, yc), r, r);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawCircle_using_Circle_using_QPointF()
  {
    QPointF c(150.999, 123.231);
    int r = 39;
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawCircle(c, r, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawEllipse(c, r, r);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawEllipse_using_axis_aligned_bounding_box()
  {
    // Axis-aligned ellipse defined by the following axis-aligned bounding box.
    int x = 150, y = 123;
    int w = 39, h = 100;
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawEllipse(x, y, w, h, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawEllipse(x, y, w, h);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawEllipse_with_center_and_semi_axes_and_orientation()
  {
    QPointF center(123.123, 156.123);
    qreal r1 = 100.13, r2 = 40.12;
    qreal oriDegree = 48.65;
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawEllipse(center, r1, r2, oriDegree, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.translate(center);
    painter_.rotate(oriDegree);
    painter_.translate(-r1, -r2);
    painter_.drawEllipse(QRectF(0, 0, 2*r1, 2*r2));

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawRect()
  {
    int x = 150, y = 123;
    int w = 39, h = 100;
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->clear();
    test_window_->drawRect(x, y, w, h, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawRect(x, y, w, h);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawPoly()
  {
    QPolygonF polygon;
    polygon << QPointF(10, 10) << QPointF(250, 20) << QPointF(150, 258);

    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawPoly(polygon, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawPolygon(polygon);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawText()
  {
    // What text.
    QString text("DO-CV is awesome!");
    // Where.
    int x = 130, y = 150;
    double orientation = 36.6;
    // Style.
    QColor color(0, 255, 123);
    int fontSize = 15;
    bool italic = true;
    bool bold = true;
    bool underline = true;

    test_window_->drawText(x, y, text, color, fontSize, orientation, italic,
                           bold, underline);

    painter_.setPen(color);

    QFont font;
    font.setPointSize(fontSize);
    font.setItalic(italic);
    font.setBold(bold);
    font.setUnderline(underline);
    painter_.setFont(font);

    painter_.translate(x, y);
    painter_.rotate(orientation);
    painter_.drawText(0, 0, text);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  // TODO: make this test more exhaustive.
  void test_drawArrow()
  {
    int x1 = 150, y1 = 150;
    int x2 = 200, y2 = 200;

    QColor color(0, 255, 123);
    int arrowWidth = 3, arrowHeight = 5;
    int style = 0;
    int width = 3;

    test_window_->drawArrow(x1, y1, x2, y2, color, arrowWidth, arrowHeight,
                            style, width);

    QImage image(get_image_from_window());
    for (int x = 150; x < 200; ++x)
      QCOMPARE(image.pixel(x,x), color.rgb());
  }

  void test_display()
  {
    int w = 20, h = 20;
    QImage patch(QSize(w, h), QImage::Format_RGB32);
    patch.fill(Qt::red);
    int x_offset = 20, y_offset = 30;
    double zoom_factor = 2.;

    test_window_->display(patch, x_offset, y_offset, zoom_factor);

    QImage image(get_image_from_window());
    for (int y = 0; y < image.height(); ++y)
      for (int x = 0; x < image.width(); ++x)
        if ( x >= x_offset && x < x_offset+zoom_factor*w &&
             y >= y_offset && y < y_offset+zoom_factor*h )
          QCOMPARE(image.pixel(x, y), QColor(Qt::red).rgb());
        else
          QCOMPARE(image.pixel(x, y), QColor(Qt::white).rgb());
  }
};

class TestPaintingWindowEvents: public QObject
{
  Q_OBJECT

private:
  PaintingWindow *test_window_;
  QPoint mouse_pos_;
  Qt::Key key_;
  int mouse_buttons_type_id_;
  int event_type_id_;

  void compare_spied_mouse_event_arguments(QSignalSpy& spy) const
  {
    QCOMPARE(spy.count(), 1);

    QList<QVariant> arguments = spy.takeFirst();
    QCOMPARE(arguments.at(0).toInt(), mouse_pos_.x());
    QCOMPARE(arguments.at(1).toInt(), mouse_pos_.y());
    QCOMPARE(static_cast<Qt::MouseButtons>(arguments.at(2).toInt()),
             Qt::NoButton);
  }

  void compare_spied_key_event_arguments(QSignalSpy& spy) const
  {
    QCOMPARE(spy.count(), 1);

    QList<QVariant> arguments = spy.takeFirst();
    QCOMPARE(static_cast<Qt::Key>(arguments.at(0).toInt()), key_);
  }

private slots:
  void initTestCase()
  {
    mouse_buttons_type_id_ = qRegisterMetaType<Qt::MouseButtons>("Qt::MouseButtons");
    event_type_id_ = qRegisterMetaType<Event>("Event");
    test_window_ = new PaintingWindow(300, 300);
    mouse_pos_ = QPoint(10, 10);
    key_ = Qt::Key_A;
  }

  void cleanupTestCase()
  {
    test_window_->deleteLater();
  }

  void test_mouse_move_event()
  {
    test_window_->setMouseTracking(true);
    QSignalSpy spy(test_window_,
                   SIGNAL(movedMouse(int, int, Qt::MouseButtons)));
    QVERIFY(spy.isValid());

    QTestEventList events;
    int x = 10, y = 10;
#ifndef _WIN32
    events.addMouseMove(QPoint(x-1, y-1));
#endif
    events.addMouseMove(QPoint(x, y));
    events.simulate(test_window_);

    QTest::mouseMove(test_window_, mouse_pos_);
    compare_spied_mouse_event_arguments(spy);
  }

  void test_mouse_press_event()
  {
    QSignalSpy spy(test_window_,
                   SIGNAL(pressedMouseButtons(int, int, Qt::MouseButtons)));
    QVERIFY(spy.isValid());

    QTest::mousePress(test_window_, Qt::LeftButton, Qt::NoModifier, mouse_pos_);
    compare_spied_mouse_event_arguments(spy);
  }

  void test_mouse_release_event()
  {
    QSignalSpy spy(test_window_,
                   SIGNAL(releasedMouseButtons(int, int, Qt::MouseButtons)));
    QVERIFY(spy.isValid());

    QTest::mouseRelease(test_window_, Qt::LeftButton, Qt::NoModifier, mouse_pos_);
    compare_spied_mouse_event_arguments(spy);
  }

  void test_key_press_event()
  {
    QSignalSpy spy(test_window_, SIGNAL(pressedKey(int)));
    QVERIFY(spy.isValid());

    QTest::keyPress(test_window_, key_, Qt::NoModifier);
    compare_spied_key_event_arguments(spy);
  }

  void test_key_release_event()
  {
    QSignalSpy spy(test_window_, SIGNAL(releasedKey(int)));
    QVERIFY(spy.isValid());

    QTest::keyRelease(test_window_, key_, Qt::NoModifier);
    compare_spied_key_event_arguments(spy);
  }

  void test_send_event()
  {
    QSignalSpy spy(test_window_, SIGNAL(sendEvent(Event)));
    QVERIFY(spy.isValid());

    QMetaObject::invokeMethod(test_window_, "waitForEvent",
                              Qt::DirectConnection, Q_ARG(int, 1));

    QTest::qWait(100);

    // Nothing happens.
    QCOMPARE(spy.count(), 1);
    QList<QVariant> arguments = spy.takeFirst();
    QVariant arg = arguments.at(0);
    arg.convert(event_type_id_);
    Event event(arguments.at(0).value<Event>());
    QCOMPARE(event.type, DO::NO_EVENT);
  }

};

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);
  app.setAttribute(Qt::AA_Use96Dpi, true);
  QTEST_DISABLE_KEYPAD_NAVIGATION;

  int num_failed_tests = 0;

  TestPaintingWindowConstructors test_painting_window_constructors;
  num_failed_tests += QTest::qExec(&test_painting_window_constructors);

  TestPaintingWindowDrawMethods test_painting_window_draw_methods;
  num_failed_tests += QTest::qExec(&test_painting_window_draw_methods);

  TestPaintingWindowEvents test_painting_window_events;
  num_failed_tests += QTest::qExec(&test_painting_window_events);

  return num_failed_tests;
}

#include "test_painting_window.moc"
<commit_msg>MAINT: used `QSignalSpy::wait()` instead of `QTest::qWait()`.<commit_after>#include <QtTest>
#include <QtWidgets>
#include <DO/Graphics/DerivedQObjects/PaintingWindow.hpp>

Q_DECLARE_METATYPE(DO::Event);

using namespace DO;

class TestPaintingWindowConstructors: public QObject
{
  Q_OBJECT

private slots:
  void test_construction_of_PaintingWindow_with_small_size()
  {
    int width = 50;
    int height = 50;
    QString windowName = "painting window";
    int x = 200;
    int y = 300;

    PaintingWindow *window = new PaintingWindow(width, height,
                                                windowName,
                                                x, y);

    QCOMPARE(window->width(), width);
    QCOMPARE(window->height(), height);
    QCOMPARE(window->windowTitle(), windowName);
#ifndef __APPLE__
    // Strangely, when we have 2 monitor screens, this fails on Mac OS X...
    QCOMPARE(window->x(), x);
    QCOMPARE(window->y(), y);
#endif
    QVERIFY(window->isVisible());

    window->deleteLater();
  }

  void test_construction_of_PaintingWindow_with_size_larger_than_desktop()
  {
    int width = qApp->desktop()->width();
    int height = qApp->desktop()->height();

    PaintingWindow *window = new PaintingWindow(width, height);

    QVERIFY(window->scrollArea()->isMaximized());
    QVERIFY(window->isVisible());

    window->deleteLater();
  }
};

class TestPaintingWindowDrawMethods: public QObject
{
  Q_OBJECT

private: // data members
  PaintingWindow *test_window_;
  QImage true_image_;
  QPainter painter_;

private: // methods
  QImage get_image_from_window()
  {
    QImage image(test_window_->size(), QImage::Format_RGB32);
    test_window_->render(&image, QPoint(), QRegion(QRect(QPoint(),
                         test_window_->size())));
    return image;
  }

private slots:
  void initTestCase()
  {
    test_window_ = new PaintingWindow(300, 300);
    true_image_ =  QImage(test_window_->size(), QImage::Format_RGB32);
  }

  void init()
  {
    test_window_->clear();
    true_image_.fill(Qt::white);
    painter_.begin(&true_image_);
  }

  void cleanup()
  {
    painter_.end();
  }

  void cleanupTestCase()
  {
    test_window_->deleteLater();
  }

  void test_drawPoint_using_integer_coordinates()
  {
    int x = 150, y = 100;
    QColor color(255, 0, 0);

    test_window_->drawPoint(x, y, color);

    painter_.setPen(color);
    painter_.drawPoint(QPoint(x, y));

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawPoint_using_QPointF()
  {
    double x = 150.95, y = 100.3333;
    QColor color(0, 225, 0);

    test_window_->drawPoint(QPointF(x, y), color);

    painter_.setPen(color);
    painter_.drawPoint(QPointF(x, y));

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawLine_using_integer_coordinates()
  {
    int x1 = 100, y1 = 100;
    int x2 = 200, y2 = 240;
    QColor color(0, 255, 0);
    int thickness = 3;

    test_window_->drawLine(x1, y1, x2, y2, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawLine(x1, y1, x2, y2);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawLine_using_QPointF()
  {
    QPointF p1(100.350, 100.699);
    QPointF p2(203.645, 240.664);
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawLine(p1, p2, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawLine(p1, p2);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawCircle_using_integer_coordinates()
  {
    int xc = 150, yc = 123;
    int r = 39;
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawCircle(xc, yc, r, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawEllipse(QPoint(xc, yc), r, r);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawCircle_using_Circle_using_QPointF()
  {
    QPointF c(150.999, 123.231);
    int r = 39;
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawCircle(c, r, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawEllipse(c, r, r);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawEllipse_using_axis_aligned_bounding_box()
  {
    // Axis-aligned ellipse defined by the following axis-aligned bounding box.
    int x = 150, y = 123;
    int w = 39, h = 100;
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawEllipse(x, y, w, h, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawEllipse(x, y, w, h);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawEllipse_with_center_and_semi_axes_and_orientation()
  {
    QPointF center(123.123, 156.123);
    qreal r1 = 100.13, r2 = 40.12;
    qreal oriDegree = 48.65;
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawEllipse(center, r1, r2, oriDegree, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.translate(center);
    painter_.rotate(oriDegree);
    painter_.translate(-r1, -r2);
    painter_.drawEllipse(QRectF(0, 0, 2*r1, 2*r2));

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawRect()
  {
    int x = 150, y = 123;
    int w = 39, h = 100;
    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->clear();
    test_window_->drawRect(x, y, w, h, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawRect(x, y, w, h);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawPoly()
  {
    QPolygonF polygon;
    polygon << QPointF(10, 10) << QPointF(250, 20) << QPointF(150, 258);

    QColor color(0, 255, 123);
    int thickness = 4;

    test_window_->drawPoly(polygon, color, thickness);

    painter_.setPen(QPen(color, thickness));
    painter_.drawPolygon(polygon);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  void test_drawText()
  {
    // What text.
    QString text("DO-CV is awesome!");
    // Where.
    int x = 130, y = 150;
    double orientation = 36.6;
    // Style.
    QColor color(0, 255, 123);
    int fontSize = 15;
    bool italic = true;
    bool bold = true;
    bool underline = true;

    test_window_->drawText(x, y, text, color, fontSize, orientation, italic,
                           bold, underline);

    painter_.setPen(color);

    QFont font;
    font.setPointSize(fontSize);
    font.setItalic(italic);
    font.setBold(bold);
    font.setUnderline(underline);
    painter_.setFont(font);

    painter_.translate(x, y);
    painter_.rotate(orientation);
    painter_.drawText(0, 0, text);

    QCOMPARE(get_image_from_window(), true_image_);
  }

  // TODO: make this test more exhaustive.
  void test_drawArrow()
  {
    int x1 = 150, y1 = 150;
    int x2 = 200, y2 = 200;

    QColor color(0, 255, 123);
    int arrowWidth = 3, arrowHeight = 5;
    int style = 0;
    int width = 3;

    test_window_->drawArrow(x1, y1, x2, y2, color, arrowWidth, arrowHeight,
                            style, width);

    QImage image(get_image_from_window());
    for (int x = 150; x < 200; ++x)
      QCOMPARE(image.pixel(x,x), color.rgb());
  }

  void test_display()
  {
    int w = 20, h = 20;
    QImage patch(QSize(w, h), QImage::Format_RGB32);
    patch.fill(Qt::red);
    int x_offset = 20, y_offset = 30;
    double zoom_factor = 2.;

    test_window_->display(patch, x_offset, y_offset, zoom_factor);

    QImage image(get_image_from_window());
    for (int y = 0; y < image.height(); ++y)
      for (int x = 0; x < image.width(); ++x)
        if ( x >= x_offset && x < x_offset+zoom_factor*w &&
             y >= y_offset && y < y_offset+zoom_factor*h )
          QCOMPARE(image.pixel(x, y), QColor(Qt::red).rgb());
        else
          QCOMPARE(image.pixel(x, y), QColor(Qt::white).rgb());
  }
};

class TestPaintingWindowEvents: public QObject
{
  Q_OBJECT

private:
  PaintingWindow *test_window_;
  QPoint mouse_pos_;
  Qt::Key key_;
  int mouse_buttons_type_id_;
  int event_type_id_;

  void compare_spied_mouse_event_arguments(QSignalSpy& spy) const
  {
    QCOMPARE(spy.count(), 1);

    QList<QVariant> arguments = spy.takeFirst();
    QCOMPARE(arguments.at(0).toInt(), mouse_pos_.x());
    QCOMPARE(arguments.at(1).toInt(), mouse_pos_.y());
    QCOMPARE(static_cast<Qt::MouseButtons>(arguments.at(2).toInt()),
             Qt::NoButton);
  }

  void compare_spied_key_event_arguments(QSignalSpy& spy) const
  {
    QCOMPARE(spy.count(), 1);

    QList<QVariant> arguments = spy.takeFirst();
    QCOMPARE(static_cast<Qt::Key>(arguments.at(0).toInt()), key_);
  }

private slots:
  void initTestCase()
  {
    mouse_buttons_type_id_ = qRegisterMetaType<Qt::MouseButtons>("Qt::MouseButtons");
    event_type_id_ = qRegisterMetaType<Event>("Event");
    test_window_ = new PaintingWindow(300, 300);
    mouse_pos_ = QPoint(10, 10);
    key_ = Qt::Key_A;
  }

  void cleanupTestCase()
  {
    test_window_->deleteLater();
  }

  void test_mouse_move_event()
  {
    test_window_->setMouseTracking(true);
    QSignalSpy spy(test_window_,
                   SIGNAL(movedMouse(int, int, Qt::MouseButtons)));
    QVERIFY(spy.isValid());

    QTestEventList events;
    int x = 10, y = 10;
#ifndef _WIN32
    events.addMouseMove(QPoint(x-1, y-1));
#endif
    events.addMouseMove(QPoint(x, y));
    events.simulate(test_window_);

    QTest::mouseMove(test_window_, mouse_pos_);
    compare_spied_mouse_event_arguments(spy);
  }

  void test_mouse_press_event()
  {
    QSignalSpy spy(test_window_,
                   SIGNAL(pressedMouseButtons(int, int, Qt::MouseButtons)));
    QVERIFY(spy.isValid());

    QTest::mousePress(test_window_, Qt::LeftButton, Qt::NoModifier, mouse_pos_);
    compare_spied_mouse_event_arguments(spy);
  }

  void test_mouse_release_event()
  {
    QSignalSpy spy(test_window_,
                   SIGNAL(releasedMouseButtons(int, int, Qt::MouseButtons)));
    QVERIFY(spy.isValid());

    QTest::mouseRelease(test_window_, Qt::LeftButton, Qt::NoModifier, mouse_pos_);
    compare_spied_mouse_event_arguments(spy);
  }

  void test_key_press_event()
  {
    QSignalSpy spy(test_window_, SIGNAL(pressedKey(int)));
    QVERIFY(spy.isValid());

    QTest::keyPress(test_window_, key_, Qt::NoModifier);
    compare_spied_key_event_arguments(spy);
  }

  void test_key_release_event()
  {
    QSignalSpy spy(test_window_, SIGNAL(releasedKey(int)));
    QVERIFY(spy.isValid());

    QTest::keyRelease(test_window_, key_, Qt::NoModifier);
    compare_spied_key_event_arguments(spy);
  }

  void test_send_event()
  {
    QSignalSpy spy(test_window_, SIGNAL(sendEvent(Event)));
    QVERIFY(spy.isValid());

    QMetaObject::invokeMethod(test_window_, "waitForEvent",
                              Qt::AutoConnection, Q_ARG(int, 1));

    // Nothing happens.
    QVERIFY(spy.wait(10));
    QCOMPARE(spy.count(), 1);
    QList<QVariant> arguments = spy.takeFirst();
    QVariant arg = arguments.at(0);
    arg.convert(event_type_id_);
    Event event(arguments.at(0).value<Event>());
    QCOMPARE(event.type, DO::NO_EVENT);
  }

};

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);
  app.setAttribute(Qt::AA_Use96Dpi, true);
  QTEST_DISABLE_KEYPAD_NAVIGATION;

  int num_failed_tests = 0;

  TestPaintingWindowConstructors test_painting_window_constructors;
  num_failed_tests += QTest::qExec(&test_painting_window_constructors);

  TestPaintingWindowDrawMethods test_painting_window_draw_methods;
  num_failed_tests += QTest::qExec(&test_painting_window_draw_methods);

  TestPaintingWindowEvents test_painting_window_events;
  num_failed_tests += QTest::qExec(&test_painting_window_events);

  return num_failed_tests;
}

#include "test_painting_window.moc"
<|endoftext|>


<commit_before>/*
 * gallerytiltshiftplugin.cpp
 *
 * Copyright (C) 2012 Igalia, S.L.
 * Author: Simon Pena <spena@igalia.com>
 *
 * This file is part of the Gallery Tilt Shift Plugin.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see http://www.gnu.org/licenses/ *
 */

#include "gallerytiltshiftplugin.h"
#include "gallerytiltshiftplugin_p.h"
#include "gallerytiltshiftwidget.h"

#include <galleryedituiprovider.h>

#include <MApplication>
#include <MMessageBox>
#include <MBanner>
#include <MLabel>
#include <MLibrary>
#include <MSceneWindow>
#include <QuillImageFilter>
#include <QDesktopServices>
#include <QGraphicsLinearLayout>
#include <QGraphicsSceneMouseEvent>
#include <QUrl>
#include <QTextOption>

static const int PORTRAIT_HEIGHT        = 224;
static const int LANDSCAPE_HEIGHT       = 170;
static const int TAP_DISTANCE           = 20;
static const int INFO_BANNER_TIMEOUT    = 2000;

M_LIBRARY

GalleryTiltShiftPluginPrivate::GalleryTiltShiftPluginPrivate() :
    m_focusPosition(),
    m_validImage(true)
{
}

GalleryTiltShiftPluginPrivate::~GalleryTiltShiftPluginPrivate()
{
}

GalleryTiltShiftPlugin::GalleryTiltShiftPlugin(QObject* parent):
    GalleryEditPlugin(parent),
    d_ptr(new GalleryTiltShiftPluginPrivate())
{
}

GalleryTiltShiftPlugin::~GalleryTiltShiftPlugin()
{
    delete d_ptr;
}

QString GalleryTiltShiftPlugin::name() const
{
    return QString("Tilt Shift");
}

int GalleryTiltShiftPlugin::menuIndex() const
{
    return 10;
}

QString GalleryTiltShiftPlugin::iconID() const
{
    // XXX: Gallery plugins load their icons by ID, so they must be placed in a
    // location watched by the MThemeDaemon. However, it seems that the MThemeDaemon
    // only re-scans its content when booting. As a consequence, the red icon
    // corresponding to a missing resource will appear until the device is rebooted.
    return QString("icon-m-image-edit-tilt-shift");
}

bool GalleryTiltShiftPlugin::containsUi() const
{
    return true;
}

void GalleryTiltShiftPlugin::paint(QPainter* painter,
                                   const QStyleOptionGraphicsItem* option,
                                   QWidget* widget)
{
    Q_UNUSED(painter)
    Q_UNUSED(option)
    Q_UNUSED(widget)

    if (editUiProvider() && toolBarWidget()) {
        GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
        if (widget->isSliderHandleLabelVisible()) {
            const QString text = QString("%L1").arg(widget->sliderValue());
            widget->setSliderHandleText(text);
        }
    }
}

QGraphicsWidget* GalleryTiltShiftPlugin::createToolBarWidget(QGraphicsItem* parent)
{
    GalleryTiltShiftWidget* pluginWidget = new GalleryTiltShiftWidget(parent);
    connect(pluginWidget, SIGNAL(applicationOrientationChanged()),
            SLOT(performEditOperation()));
    connect(pluginWidget, SIGNAL(sliderValueChanged()),
            SLOT(performEditOperation()));
    connect(pluginWidget, SIGNAL(aboutLinkActivated(QString)),
            SLOT(onAboutLinkActivated(QString)));
    return pluginWidget;
}

bool GalleryTiltShiftPlugin::receiveMouseEvent(QGraphicsSceneMouseEvent* event)
{
    if (event &&
            event->type() == QEvent::GraphicsSceneMouseRelease &&
            event->button() == Qt::LeftButton &&
            (event->scenePos() - event->buttonDownScenePos(Qt::LeftButton)).manhattanLength() < TAP_DISTANCE) {
        Q_D(GalleryTiltShiftPlugin);
        if (d->m_validImage) {
            d->m_focusPosition.setX(event->pos().toPoint().x());
            d->m_focusPosition.setY(event->pos().toPoint().y());
            performEditOperation();
            return true;
        } else {
            showInfoBanner("Plugin disabled for this image size");
        }
    }
    return false;
}

const QSize GalleryTiltShiftPlugin::toolBarWidgetSize(const M::Orientation& orientation) const
{
    QSize size = GalleryEditPlugin::toolBarWidgetSize(orientation);

    if (M::Portrait == orientation) {
        size.setHeight(PORTRAIT_HEIGHT);
    } else {
        size.setHeight(LANDSCAPE_HEIGHT);
    }

    return size;
}

void GalleryTiltShiftPlugin::performEditOperation()
{
    if (editUiProvider()) {
        Q_D(GalleryTiltShiftPlugin);
        GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
        const QPoint imagePoint = editUiProvider()->convertScreenCoordToImageCoord(d->m_focusPosition);
        if (imagePoint != QPoint(-1,-1)) {
            QHash<QuillImageFilter::QuillFilterOption, QVariant> optionHash;
            optionHash.insert(QuillImageFilter::Center,
                              imagePoint);
            optionHash.insert(QuillImageFilter::Radius,
                              QVariant(widget->sliderValue()));
            optionHash.insert(QuillImageFilter::Horizontal,
                              QVariant(widget->applicationOrientation() == Qt::Horizontal));
            editUiProvider()->runEditFilter("com.igalia.tiltshift", optionHash, true);
            emit editOperationPerformed();
        }
    }
}

void GalleryTiltShiftPlugin::activate()
{
    if (editUiProvider()) {
        Q_D(GalleryTiltShiftPlugin);
        d->m_validImage = editUiProvider()->fullImageSize().height() <= 512 &&
                editUiProvider()->fullImageSize().width() <= 512;
        if (d->m_validImage) {
            showInfoBanner("Tap on an area to keep it focused");
        } else {
            showMessageBox("Tilt Shift plugin limitations",
                           "Gallery Tilt Shift plugin is currently limited to "
                           "small images (512x512)
"
                           "For a given image:"
                           "
"
                           "
Scale it or crop it
"
                           "
Save it with a different name
"
                           "
Apply the filter to the new one
"
                           "
");
            GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
            widget->setEnabled(d->m_validImage);
        }
    }
}

void GalleryTiltShiftPlugin::onAboutLinkActivated(const QString &link)
{
    Q_UNUSED(link)
    if (link.toLower().startsWith("http") || link.toLower().startsWith("mailto")) {
        QDesktopServices::openUrl(QUrl(link));
    } else {
        showMessageBox("Tilt Shift plugin - 0.1.0",
                       "Copyright (c) 2012 Igalia S.L."
                       "

"
                       "spena@igalia.com | "
                       "www.igalia.com"
                       "

"
                       "This library is free software; you can redistribute it and/or "
                       "modify it under the terms of the GNU Lesser General Public License "
                       "as published by the Free Software Foundation; version 2.1 of "
                       "the License, or (at your option) any later version.");
    }
}

MMessageBox* GalleryTiltShiftPlugin::showMessageBox(const QString& title, const QString& text) const
{
    MMessageBox* messageBox = new MMessageBox(title, "");
    MLabel* innerLabel = new MLabel(messageBox);
    innerLabel->setWordWrap(true);
    innerLabel->setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
    innerLabel->setStyleName("CommonQueryText");
    innerLabel->setText(text);
    innerLabel->setAlignment(Qt::AlignHCenter);
    messageBox->setCentralWidget(innerLabel);

    connect(innerLabel, SIGNAL(linkActivated(QString)),
            this, SLOT(onAboutLinkActivated(QString)));
    connect(this, SIGNAL(deactivated()),
            messageBox, SLOT(disappear()));

    messageBox->appear(MSceneWindow::DestroyWhenDone);

    return messageBox;
}

MBanner* GalleryTiltShiftPlugin::showInfoBanner(const QString& title) const
{
    MBanner *infoBanner = new MBanner;
    infoBanner->setTitle(title);
    infoBanner->setStyleName(MBannerType::InformationBanner);
    infoBanner->model()->setDisappearTimeout(2000);
    connect(this, SIGNAL(deactivated()),
            infoBanner, SLOT(disappear()));

    infoBanner->appear(MApplication::activeWindow(), MSceneWindow::DestroyWhenDone);

    return infoBanner;
}

Q_EXPORT_PLUGIN2(gallerytiltshiftplugin, GalleryTiltShiftPlugin)

<commit_msg>Prepare for 0.1.1 release<commit_after>/*
 * gallerytiltshiftplugin.cpp
 *
 * Copyright (C) 2012 Igalia, S.L.
 * Author: Simon Pena <spena@igalia.com>
 *
 * This file is part of the Gallery Tilt Shift Plugin.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see http://www.gnu.org/licenses/ *
 */

#include "gallerytiltshiftplugin.h"
#include "gallerytiltshiftplugin_p.h"
#include "gallerytiltshiftwidget.h"

#include <galleryedituiprovider.h>

#include <MApplication>
#include <MMessageBox>
#include <MBanner>
#include <MLabel>
#include <MLibrary>
#include <MSceneWindow>
#include <QuillImageFilter>
#include <QDesktopServices>
#include <QGraphicsLinearLayout>
#include <QGraphicsSceneMouseEvent>
#include <QUrl>
#include <QTextOption>

static const int PORTRAIT_HEIGHT        = 224;
static const int LANDSCAPE_HEIGHT       = 170;
static const int TAP_DISTANCE           = 20;
static const int INFO_BANNER_TIMEOUT    = 2000;

M_LIBRARY

GalleryTiltShiftPluginPrivate::GalleryTiltShiftPluginPrivate() :
    m_focusPosition(),
    m_validImage(true)
{
}

GalleryTiltShiftPluginPrivate::~GalleryTiltShiftPluginPrivate()
{
}

GalleryTiltShiftPlugin::GalleryTiltShiftPlugin(QObject* parent):
    GalleryEditPlugin(parent),
    d_ptr(new GalleryTiltShiftPluginPrivate())
{
}

GalleryTiltShiftPlugin::~GalleryTiltShiftPlugin()
{
    delete d_ptr;
}

QString GalleryTiltShiftPlugin::name() const
{
    return QString("Tilt Shift");
}

int GalleryTiltShiftPlugin::menuIndex() const
{
    return 10;
}

QString GalleryTiltShiftPlugin::iconID() const
{
    // XXX: Gallery plugins load their icons by ID, so they must be placed in a
    // location watched by the MThemeDaemon. However, it seems that the MThemeDaemon
    // only re-scans its content when booting. As a consequence, the red icon
    // corresponding to a missing resource will appear until the device is rebooted.
    return QString("icon-m-image-edit-tilt-shift");
}

bool GalleryTiltShiftPlugin::containsUi() const
{
    return true;
}

void GalleryTiltShiftPlugin::paint(QPainter* painter,
                                   const QStyleOptionGraphicsItem* option,
                                   QWidget* widget)
{
    Q_UNUSED(painter)
    Q_UNUSED(option)
    Q_UNUSED(widget)

    if (editUiProvider() && toolBarWidget()) {
        GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
        if (widget->isSliderHandleLabelVisible()) {
            const QString text = QString("%L1").arg(widget->sliderValue());
            widget->setSliderHandleText(text);
        }
    }
}

QGraphicsWidget* GalleryTiltShiftPlugin::createToolBarWidget(QGraphicsItem* parent)
{
    GalleryTiltShiftWidget* pluginWidget = new GalleryTiltShiftWidget(parent);
    connect(pluginWidget, SIGNAL(applicationOrientationChanged()),
            SLOT(performEditOperation()));
    connect(pluginWidget, SIGNAL(sliderValueChanged()),
            SLOT(performEditOperation()));
    connect(pluginWidget, SIGNAL(aboutLinkActivated(QString)),
            SLOT(onAboutLinkActivated(QString)));
    return pluginWidget;
}

bool GalleryTiltShiftPlugin::receiveMouseEvent(QGraphicsSceneMouseEvent* event)
{
    if (event &&
            event->type() == QEvent::GraphicsSceneMouseRelease &&
            event->button() == Qt::LeftButton &&
            (event->scenePos() - event->buttonDownScenePos(Qt::LeftButton)).manhattanLength() < TAP_DISTANCE) {
        Q_D(GalleryTiltShiftPlugin);
        if (d->m_validImage) {
            d->m_focusPosition.setX(event->pos().toPoint().x());
            d->m_focusPosition.setY(event->pos().toPoint().y());
            performEditOperation();
            return true;
        } else {
            showInfoBanner("Plugin disabled for this image size");
        }
    }
    return false;
}

const QSize GalleryTiltShiftPlugin::toolBarWidgetSize(const M::Orientation& orientation) const
{
    QSize size = GalleryEditPlugin::toolBarWidgetSize(orientation);

    if (M::Portrait == orientation) {
        size.setHeight(PORTRAIT_HEIGHT);
    } else {
        size.setHeight(LANDSCAPE_HEIGHT);
    }

    return size;
}

void GalleryTiltShiftPlugin::performEditOperation()
{
    if (editUiProvider()) {
        Q_D(GalleryTiltShiftPlugin);
        GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
        const QPoint imagePoint = editUiProvider()->convertScreenCoordToImageCoord(d->m_focusPosition);
        if (imagePoint != QPoint(-1,-1)) {
            QHash<QuillImageFilter::QuillFilterOption, QVariant> optionHash;
            optionHash.insert(QuillImageFilter::Center,
                              imagePoint);
            optionHash.insert(QuillImageFilter::Radius,
                              QVariant(widget->sliderValue()));
            optionHash.insert(QuillImageFilter::Horizontal,
                              QVariant(widget->applicationOrientation() == Qt::Horizontal));
            editUiProvider()->runEditFilter("com.igalia.tiltshift", optionHash, true);
            emit editOperationPerformed();
        }
    }
}

void GalleryTiltShiftPlugin::activate()
{
    if (editUiProvider()) {
        Q_D(GalleryTiltShiftPlugin);
        d->m_validImage = editUiProvider()->fullImageSize().height() <= 512 &&
                editUiProvider()->fullImageSize().width() <= 512;
        if (d->m_validImage) {
            showInfoBanner("Tap on an area to keep it focused");
        } else {
            showMessageBox("Tilt Shift plugin limitations",
                           "Gallery Tilt Shift plugin is currently limited to "
                           "small images (512x512)
"
                           "For a given image:"
                           "
"
                           "
Scale it or crop it
"
                           "
Save it with a different name
"
                           "
Apply the filter to the new one
"
                           "
");
            GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
            widget->setEnabled(d->m_validImage);
        }
    }
}

void GalleryTiltShiftPlugin::onAboutLinkActivated(const QString &link)
{
    Q_UNUSED(link)
    if (link.toLower().startsWith("http") || link.toLower().startsWith("mailto")) {
        QDesktopServices::openUrl(QUrl(link));
    } else {
        showMessageBox("Tilt Shift plugin - 0.1.1",
                       "Copyright (c) 2012 Igalia S.L."
                       "

"
                       "spena@igalia.com | "
                       "www.igalia.com"
                       "

"
                       "This library is free software; you can redistribute it and/or "
                       "modify it under the terms of the GNU Lesser General Public License "
                       "as published by the Free Software Foundation; version 2.1 of "
                       "the License, or (at your option) any later version.");
    }
}

MMessageBox* GalleryTiltShiftPlugin::showMessageBox(const QString& title, const QString& text) const
{
    MMessageBox* messageBox = new MMessageBox(title, "");
    MLabel* innerLabel = new MLabel(messageBox);
    innerLabel->setWordWrap(true);
    innerLabel->setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
    innerLabel->setStyleName("CommonQueryText");
    innerLabel->setText(text);
    innerLabel->setAlignment(Qt::AlignHCenter);
    messageBox->setCentralWidget(innerLabel);

    connect(innerLabel, SIGNAL(linkActivated(QString)),
            this, SLOT(onAboutLinkActivated(QString)));
    connect(this, SIGNAL(deactivated()),
            messageBox, SLOT(disappear()));

    messageBox->appear(MSceneWindow::DestroyWhenDone);

    return messageBox;
}

MBanner* GalleryTiltShiftPlugin::showInfoBanner(const QString& title) const
{
    MBanner *infoBanner = new MBanner;
    infoBanner->setTitle(title);
    infoBanner->setStyleName(MBannerType::InformationBanner);
    infoBanner->model()->setDisappearTimeout(2000);
    connect(this, SIGNAL(deactivated()),
            infoBanner, SLOT(disappear()));

    infoBanner->appear(MApplication::activeWindow(), MSceneWindow::DestroyWhenDone);

    return infoBanner;
}

Q_EXPORT_PLUGIN2(gallerytiltshiftplugin, GalleryTiltShiftPlugin)

<|endoftext|>


<commit_before>/*
 * gallerytiltshiftplugin.cpp
 *
 * Copyright (C) 2012 Igalia, S.L.
 * Author: Simon Pena <spena@igalia.com>
 *
 * This file is part of the Gallery Tilt Shift Plugin.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see http://www.gnu.org/licenses/ *
 */

#include "gallerytiltshiftplugin.h"
#include "gallerytiltshiftplugin_p.h"
#include "gallerytiltshiftwidget.h"

#include <galleryedituiprovider.h>

#include <MApplication>
#include <MMessageBox>
#include <MBanner>
#include <MLabel>
#include <MLibrary>
#include <MSceneWindow>
#include <QuillImageFilter>
#include <QDesktopServices>
#include <QGraphicsLinearLayout>
#include <QGraphicsSceneMouseEvent>
#include <QUrl>
#include <QTextOption>

static const int PORTRAIT_HEIGHT        = 224;
static const int LANDSCAPE_HEIGHT       = 170;
static const int TAP_DISTANCE           = 20;
static const int INFO_BANNER_TIMEOUT    = 2000;

M_LIBRARY

GalleryTiltShiftPluginPrivate::GalleryTiltShiftPluginPrivate() :
    m_focusPosition(),
    m_validImage(true)
{
}

GalleryTiltShiftPluginPrivate::~GalleryTiltShiftPluginPrivate()
{
}

GalleryTiltShiftPlugin::GalleryTiltShiftPlugin(QObject* parent):
    GalleryEditPlugin(parent),
    d_ptr(new GalleryTiltShiftPluginPrivate())
{
}

GalleryTiltShiftPlugin::~GalleryTiltShiftPlugin()
{
    delete d_ptr;
}

QString GalleryTiltShiftPlugin::name() const
{
    return QString("Tilt Shift");
}

int GalleryTiltShiftPlugin::menuIndex() const
{
    return 10;
}

QString GalleryTiltShiftPlugin::iconID() const
{
    // XXX: Gallery plugins load their icons by ID, so they must be placed in a
    // location watched by the MThemeDaemon. However, it seems that the MThemeDaemon
    // only re-scans its content when booting. As a consequence, the red icon
    // corresponding to a missing resource will appear until the device is rebooted.
    return QString("icon-m-image-edit-tilt-shift");
}

bool GalleryTiltShiftPlugin::containsUi() const
{
    return true;
}

void GalleryTiltShiftPlugin::paint(QPainter* painter,
                                   const QStyleOptionGraphicsItem* option,
                                   QWidget* widget)
{
    Q_UNUSED(painter)
    Q_UNUSED(option)
    Q_UNUSED(widget)

    if (editUiProvider() && toolBarWidget()) {
        GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
        if (widget->isSliderHandleLabelVisible()) {
            const QString text = QString("%L1").arg(widget->sliderValue());
            widget->setSliderHandleText(text);
        }
    }
}

QGraphicsWidget* GalleryTiltShiftPlugin::createToolBarWidget(QGraphicsItem* parent)
{
    GalleryTiltShiftWidget* pluginWidget = new GalleryTiltShiftWidget(parent);
    connect(pluginWidget, SIGNAL(applicationOrientationChanged()),
            SLOT(performEditOperation()));
    connect(pluginWidget, SIGNAL(sliderValueChanged()),
            SLOT(performEditOperation()));
    connect(pluginWidget, SIGNAL(aboutLinkActivated(QString)),
            SLOT(onAboutLinkActivated(QString)));
    return pluginWidget;
}

bool GalleryTiltShiftPlugin::receiveMouseEvent(QGraphicsSceneMouseEvent* event)
{
    if (event &&
            event->type() == QEvent::GraphicsSceneMouseRelease &&
            event->button() == Qt::LeftButton &&
            (event->scenePos() - event->buttonDownScenePos(Qt::LeftButton)).manhattanLength() < TAP_DISTANCE) {
        Q_D(GalleryTiltShiftPlugin);
        if (d->m_validImage) {
            d->m_focusPosition.setX(event->pos().toPoint().x());
            d->m_focusPosition.setY(event->pos().toPoint().y());
            performEditOperation();
            return true;
        } else {
            showInfoBanner("Plugin disabled for this image size");
        }
    }
    return false;
}

const QSize GalleryTiltShiftPlugin::toolBarWidgetSize(const M::Orientation& orientation) const
{
    QSize size = GalleryEditPlugin::toolBarWidgetSize(orientation);

    if (M::Portrait == orientation) {
        size.setHeight(PORTRAIT_HEIGHT);
    } else {
        size.setHeight(LANDSCAPE_HEIGHT);
    }

    return size;
}

void GalleryTiltShiftPlugin::performEditOperation()
{
    if (editUiProvider()) {
        Q_D(GalleryTiltShiftPlugin);
        GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
        const QPoint imagePoint = editUiProvider()->convertScreenCoordToImageCoord(d->m_focusPosition);
        if (imagePoint != QPoint(-1,-1)) {
            QHash<QuillImageFilter::QuillFilterOption, QVariant> optionHash;
            optionHash.insert(QuillImageFilter::Center,
                              imagePoint);
            optionHash.insert(QuillImageFilter::Radius,
                              QVariant(widget->sliderValue()));
            optionHash.insert(QuillImageFilter::Horizontal,
                              QVariant(widget->applicationOrientation() == Qt::Horizontal));
            editUiProvider()->runEditFilter("com.igalia.tiltshift", optionHash, true);
            emit editOperationPerformed();
        }
    }
}

void GalleryTiltShiftPlugin::activate()
{
    if (editUiProvider()) {
        Q_D(GalleryTiltShiftPlugin);
        d->m_validImage = editUiProvider()->fullImageSize().height() <= 512 &&
                editUiProvider()->fullImageSize().width() <= 512;
        if (d->m_validImage) {
            showInfoBanner("Tap on an area to keep it focused");
        } else {
            showMessageBox("Tilt Shift plugin limitations",
                           "Gallery Tilt Shift plugin is currently limited to "
                           "small images (512x512)
"
                           "For a given image:"
                           "
"
                           "
Scale it or crop it
"
                           "
Save it with a different name
"
                           "
Apply the filter to the new one
"
                           "
");
            GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
            widget->setEnabled(d->m_validImage);
        }
    }
}

void GalleryTiltShiftPlugin::onAboutLinkActivated(const QString &link)
{
    Q_UNUSED(link)
    if (link.toLower().startsWith("http") || link.toLower().startsWith("mailto")) {
        QDesktopServices::openUrl(QUrl(link));
    } else {
        showMessageBox("Tilt Shift plugin - 0.1.0",
                       "Copyright (c) 2012 Igalia S.L."
                       "

"
                       "spena@igalia.com | "
                       "www.igalia.com"
                       "

"
                       "This library is free software; you can redistribute it and/or "
                       "modify it under the terms of the GNU Lesser General Public License "
                       "as published by the Free Software Foundation; version 2.1 of "
                       "the License, or (at your option) any later version.");
    }
}

MMessageBox* GalleryTiltShiftPlugin::showMessageBox(const QString& title, const QString& text) const
{
    MMessageBox* messageBox = new MMessageBox(title, "");
    MLabel* innerLabel = new MLabel(messageBox);
    innerLabel->setWordWrap(true);
    innerLabel->setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
    innerLabel->setStyleName("CommonQueryText");
    innerLabel->setText(text);
    innerLabel->setAlignment(Qt::AlignHCenter);
    messageBox->setCentralWidget(innerLabel);

    connect(innerLabel, SIGNAL(linkActivated(QString)),
            this, SLOT(onAboutLinkActivated(QString)));
    connect(this, SIGNAL(deactivated()),
            messageBox, SLOT(disappear()));

    messageBox->appear(MSceneWindow::DestroyWhenDone);

    return messageBox;
}

MBanner* GalleryTiltShiftPlugin::showInfoBanner(const QString& title) const
{
    MBanner *infoBanner = new MBanner;
    infoBanner->setTitle(title);
    infoBanner->setStyleName("InformationBanner");
    infoBanner->model()->setDisappearTimeout(2000);
    connect(this, SIGNAL(deactivated()),
            infoBanner, SLOT(disappear()));

    infoBanner->appear(MApplication::activeWindow(), MSceneWindow::DestroyWhenDone);

    return infoBanner;
}

Q_EXPORT_PLUGIN2(gallerytiltshiftplugin, GalleryTiltShiftPlugin)

<commit_msg>InfoBanner uses defined constant, not hardcode one<commit_after>/*
 * gallerytiltshiftplugin.cpp
 *
 * Copyright (C) 2012 Igalia, S.L.
 * Author: Simon Pena <spena@igalia.com>
 *
 * This file is part of the Gallery Tilt Shift Plugin.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see http://www.gnu.org/licenses/ *
 */

#include "gallerytiltshiftplugin.h"
#include "gallerytiltshiftplugin_p.h"
#include "gallerytiltshiftwidget.h"

#include <galleryedituiprovider.h>

#include <MApplication>
#include <MMessageBox>
#include <MBanner>
#include <MLabel>
#include <MLibrary>
#include <MSceneWindow>
#include <QuillImageFilter>
#include <QDesktopServices>
#include <QGraphicsLinearLayout>
#include <QGraphicsSceneMouseEvent>
#include <QUrl>
#include <QTextOption>

static const int PORTRAIT_HEIGHT        = 224;
static const int LANDSCAPE_HEIGHT       = 170;
static const int TAP_DISTANCE           = 20;
static const int INFO_BANNER_TIMEOUT    = 2000;

M_LIBRARY

GalleryTiltShiftPluginPrivate::GalleryTiltShiftPluginPrivate() :
    m_focusPosition(),
    m_validImage(true)
{
}

GalleryTiltShiftPluginPrivate::~GalleryTiltShiftPluginPrivate()
{
}

GalleryTiltShiftPlugin::GalleryTiltShiftPlugin(QObject* parent):
    GalleryEditPlugin(parent),
    d_ptr(new GalleryTiltShiftPluginPrivate())
{
}

GalleryTiltShiftPlugin::~GalleryTiltShiftPlugin()
{
    delete d_ptr;
}

QString GalleryTiltShiftPlugin::name() const
{
    return QString("Tilt Shift");
}

int GalleryTiltShiftPlugin::menuIndex() const
{
    return 10;
}

QString GalleryTiltShiftPlugin::iconID() const
{
    // XXX: Gallery plugins load their icons by ID, so they must be placed in a
    // location watched by the MThemeDaemon. However, it seems that the MThemeDaemon
    // only re-scans its content when booting. As a consequence, the red icon
    // corresponding to a missing resource will appear until the device is rebooted.
    return QString("icon-m-image-edit-tilt-shift");
}

bool GalleryTiltShiftPlugin::containsUi() const
{
    return true;
}

void GalleryTiltShiftPlugin::paint(QPainter* painter,
                                   const QStyleOptionGraphicsItem* option,
                                   QWidget* widget)
{
    Q_UNUSED(painter)
    Q_UNUSED(option)
    Q_UNUSED(widget)

    if (editUiProvider() && toolBarWidget()) {
        GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
        if (widget->isSliderHandleLabelVisible()) {
            const QString text = QString("%L1").arg(widget->sliderValue());
            widget->setSliderHandleText(text);
        }
    }
}

QGraphicsWidget* GalleryTiltShiftPlugin::createToolBarWidget(QGraphicsItem* parent)
{
    GalleryTiltShiftWidget* pluginWidget = new GalleryTiltShiftWidget(parent);
    connect(pluginWidget, SIGNAL(applicationOrientationChanged()),
            SLOT(performEditOperation()));
    connect(pluginWidget, SIGNAL(sliderValueChanged()),
            SLOT(performEditOperation()));
    connect(pluginWidget, SIGNAL(aboutLinkActivated(QString)),
            SLOT(onAboutLinkActivated(QString)));
    return pluginWidget;
}

bool GalleryTiltShiftPlugin::receiveMouseEvent(QGraphicsSceneMouseEvent* event)
{
    if (event &&
            event->type() == QEvent::GraphicsSceneMouseRelease &&
            event->button() == Qt::LeftButton &&
            (event->scenePos() - event->buttonDownScenePos(Qt::LeftButton)).manhattanLength() < TAP_DISTANCE) {
        Q_D(GalleryTiltShiftPlugin);
        if (d->m_validImage) {
            d->m_focusPosition.setX(event->pos().toPoint().x());
            d->m_focusPosition.setY(event->pos().toPoint().y());
            performEditOperation();
            return true;
        } else {
            showInfoBanner("Plugin disabled for this image size");
        }
    }
    return false;
}

const QSize GalleryTiltShiftPlugin::toolBarWidgetSize(const M::Orientation& orientation) const
{
    QSize size = GalleryEditPlugin::toolBarWidgetSize(orientation);

    if (M::Portrait == orientation) {
        size.setHeight(PORTRAIT_HEIGHT);
    } else {
        size.setHeight(LANDSCAPE_HEIGHT);
    }

    return size;
}

void GalleryTiltShiftPlugin::performEditOperation()
{
    if (editUiProvider()) {
        Q_D(GalleryTiltShiftPlugin);
        GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
        const QPoint imagePoint = editUiProvider()->convertScreenCoordToImageCoord(d->m_focusPosition);
        if (imagePoint != QPoint(-1,-1)) {
            QHash<QuillImageFilter::QuillFilterOption, QVariant> optionHash;
            optionHash.insert(QuillImageFilter::Center,
                              imagePoint);
            optionHash.insert(QuillImageFilter::Radius,
                              QVariant(widget->sliderValue()));
            optionHash.insert(QuillImageFilter::Horizontal,
                              QVariant(widget->applicationOrientation() == Qt::Horizontal));
            editUiProvider()->runEditFilter("com.igalia.tiltshift", optionHash, true);
            emit editOperationPerformed();
        }
    }
}

void GalleryTiltShiftPlugin::activate()
{
    if (editUiProvider()) {
        Q_D(GalleryTiltShiftPlugin);
        d->m_validImage = editUiProvider()->fullImageSize().height() <= 512 &&
                editUiProvider()->fullImageSize().width() <= 512;
        if (d->m_validImage) {
            showInfoBanner("Tap on an area to keep it focused");
        } else {
            showMessageBox("Tilt Shift plugin limitations",
                           "Gallery Tilt Shift plugin is currently limited to "
                           "small images (512x512)
"
                           "For a given image:"
                           "
"
                           "
Scale it or crop it
"
                           "
Save it with a different name
"
                           "
Apply the filter to the new one
"
                           "
");
            GalleryTiltShiftWidget* widget = static_cast<GalleryTiltShiftWidget*>(toolBarWidget());
            widget->setEnabled(d->m_validImage);
        }
    }
}

void GalleryTiltShiftPlugin::onAboutLinkActivated(const QString &link)
{
    Q_UNUSED(link)
    if (link.toLower().startsWith("http") || link.toLower().startsWith("mailto")) {
        QDesktopServices::openUrl(QUrl(link));
    } else {
        showMessageBox("Tilt Shift plugin - 0.1.0",
                       "Copyright (c) 2012 Igalia S.L."
                       "

"
                       "spena@igalia.com | "
                       "www.igalia.com"
                       "

"
                       "This library is free software; you can redistribute it and/or "
                       "modify it under the terms of the GNU Lesser General Public License "
                       "as published by the Free Software Foundation; version 2.1 of "
                       "the License, or (at your option) any later version.");
    }
}

MMessageBox* GalleryTiltShiftPlugin::showMessageBox(const QString& title, const QString& text) const
{
    MMessageBox* messageBox = new MMessageBox(title, "");
    MLabel* innerLabel = new MLabel(messageBox);
    innerLabel->setWordWrap(true);
    innerLabel->setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
    innerLabel->setStyleName("CommonQueryText");
    innerLabel->setText(text);
    innerLabel->setAlignment(Qt::AlignHCenter);
    messageBox->setCentralWidget(innerLabel);

    connect(innerLabel, SIGNAL(linkActivated(QString)),
            this, SLOT(onAboutLinkActivated(QString)));
    connect(this, SIGNAL(deactivated()),
            messageBox, SLOT(disappear()));

    messageBox->appear(MSceneWindow::DestroyWhenDone);

    return messageBox;
}

MBanner* GalleryTiltShiftPlugin::showInfoBanner(const QString& title) const
{
    MBanner *infoBanner = new MBanner;
    infoBanner->setTitle(title);
    infoBanner->setStyleName(MBannerType::InformationBanner);
    infoBanner->model()->setDisappearTimeout(2000);
    connect(this, SIGNAL(deactivated()),
            infoBanner, SLOT(disappear()));

    infoBanner->appear(MApplication::activeWindow(), MSceneWindow::DestroyWhenDone);

    return infoBanner;
}

Q_EXPORT_PLUGIN2(gallerytiltshiftplugin, GalleryTiltShiftPlugin)

<|endoftext|>


<commit_before>//===-- Intercept.cpp - System function interception routines -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// If a function call occurs to an external function, the JIT is designed to use
// the dynamic loader interface to find a function to call.  This is useful for
// calling system calls and library functions that are not available in LLVM.
// Some system calls, however, need to be handled specially.  For this reason,
// we intercept some of them here and use our own stubs to handle them.
//
//===----------------------------------------------------------------------===//

#include "JIT.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/System/DynamicLibrary.h"
#include "llvm/Config/config.h"
using namespace llvm;

// AtExitHandlers - List of functions to call when the program exits,
// registered with the atexit() library function.
static std::vector<void (*)()> AtExitHandlers;

/// runAtExitHandlers - Run any functions registered by the program's
/// calls to atexit(3), which we intercept and store in
/// AtExitHandlers.
///
static void runAtExitHandlers() {
  while (!AtExitHandlers.empty()) {
    void (*Fn)() = AtExitHandlers.back();
    AtExitHandlers.pop_back();
    Fn();
  }
}

//===----------------------------------------------------------------------===//
// Function stubs that are invoked instead of certain library calls
//===----------------------------------------------------------------------===//

// Force the following functions to be linked in to anything that uses the
// JIT. This is a hack designed to work around the all-too-clever Glibc
// strategy of making these functions work differently when inlined vs. when
// not inlined, and hiding their real definitions in a separate archive file
// that the dynamic linker can't see. For more info, search for
// 'libc_nonshared.a' on Google, or read http://llvm.org/PR274.
#if defined(__linux__)
#if defined(HAVE_SYS_STAT_H)
#include <sys/stat.h>
#endif
#include <fcntl.h>
/* stat functions are redirecting to __xstat with a version number.  On x86-64
 * linking with libc_nonshared.a and -Wl,--export-dynamic doesn't make 'stat'
 * available as an exported symbol, so we have to add it explicitly.
 */
namespace {
class StatSymbols {
public:
  StatSymbols() {
    sys::DynamicLibrary::AddSymbol("stat", (void*)(intptr_t)stat);
    sys::DynamicLibrary::AddSymbol("fstat", (void*)(intptr_t)fstat);
    sys::DynamicLibrary::AddSymbol("lstat", (void*)(intptr_t)lstat);
    sys::DynamicLibrary::AddSymbol("stat64", (void*)(intptr_t)stat64);
    sys::DynamicLibrary::AddSymbol("\x1stat64", (void*)(intptr_t)stat64);
    sys::DynamicLibrary::AddSymbol("\x1open64", (void*)(intptr_t)open64);
    sys::DynamicLibrary::AddSymbol("\x1lseek64", (void*)(intptr_t)lseek64);
    sys::DynamicLibrary::AddSymbol("fstat64", (void*)(intptr_t)fstat64);
    sys::DynamicLibrary::AddSymbol("lstat64", (void*)(intptr_t)lstat64);
    sys::DynamicLibrary::AddSymbol("atexit", (void*)(intptr_t)atexit);
    sys::DynamicLibrary::AddSymbol("mknod", (void*)(intptr_t)mknod);
  }
};
}
static StatSymbols initStatSymbols;
#endif // __linux__

// jit_exit - Used to intercept the "exit" library call.
static void jit_exit(int Status) {
  runAtExitHandlers();   // Run atexit handlers...
  exit(Status);
}

// jit_atexit - Used to intercept the "atexit" library call.
static int jit_atexit(void (*Fn)()) {
  AtExitHandlers.push_back(Fn);    // Take note of atexit handler...
  return 0;  // Always successful
}

static int jit_noop() {
  return 0;
}

//===----------------------------------------------------------------------===//
//
/// getPointerToNamedFunction - This method returns the address of the specified
/// function by using the dynamic loader interface.  As such it is only useful
/// for resolving library symbols, not code generated symbols.
///
void *JIT::getPointerToNamedFunction(const std::string &Name,
                                     bool AbortOnFailure) {
  if (!isSymbolSearchingDisabled()) {
    // Check to see if this is one of the functions we want to intercept.  Note,
    // we cast to intptr_t here to silence a -pedantic warning that complains
    // about casting a function pointer to a normal pointer.
    if (Name == "exit") return (void*)(intptr_t)&jit_exit;
    if (Name == "atexit") return (void*)(intptr_t)&jit_atexit;

    // We shuold not invoke parent's ctors/dtors from main()! (PR3897)
    // On Mingw and Cygwin, the symbol __main is resolved to
    // callee's(eg. tools/lli) one, to invoke wrong duplicated ctors
    // (and register wrong callee's dtors with atexit(3)).
    // We expect ExecutionEngine::runStaticConstructorsDestructors()
    // is called before ExecutionEngine::runFunctionAsMain() is called.
    if (Name == "__main") return (void*)(intptr_t)&jit_noop;

    const char *NameStr = Name.c_str();
    // If this is an asm specifier, skip the sentinal.
    if (NameStr[0] == 1) ++NameStr;

    // If it's an external function, look it up in the process image...
    void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr);
    if (Ptr) return Ptr;

    // If it wasn't found and if it starts with an underscore ('_') character,
    // and has an asm specifier, try again without the underscore.
    if (Name[0] == 1 && NameStr[0] == '_') {
      Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr+1);
      if (Ptr) return Ptr;
    }

    // Darwin/PPC adds $LDBLStub suffixes to various symbols like printf.  These
    // are references to hidden visibility symbols that dlsym cannot resolve.
    // If we have one of these, strip off $LDBLStub and try again.
#if defined(__APPLE__) && defined(__ppc__)
    if (Name.size() > 9 && Name[Name.size()-9] == '$' &&
        memcmp(&Name[Name.size()-8], "LDBLStub", 8) == 0) {
      // First try turning $LDBLStub into $LDBL128. If that fails, strip it off.
      // This mirrors logic in libSystemStubs.a.
      std::string Prefix = std::string(Name.begin(), Name.end()-9);
      if (void *Ptr = getPointerToNamedFunction(Prefix+"$LDBL128", false))
        return Ptr;
      if (void *Ptr = getPointerToNamedFunction(Prefix, false))
        return Ptr;
    }
#endif
  }

  /// If a LazyFunctionCreator is installed, use it to get/create the function.
  if (LazyFunctionCreator)
    if (void *RP = LazyFunctionCreator(Name))
      return RP;

  if (AbortOnFailure) {
    report_fatal_error("Program used external function '"+Name+
                      "' which could not be resolved!");
  }
  return 0;
}
<commit_msg>Fix a comment.<commit_after>//===-- Intercept.cpp - System function interception routines -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// If a function call occurs to an external function, the JIT is designed to use
// the dynamic loader interface to find a function to call.  This is useful for
// calling system calls and library functions that are not available in LLVM.
// Some system calls, however, need to be handled specially.  For this reason,
// we intercept some of them here and use our own stubs to handle them.
//
//===----------------------------------------------------------------------===//

#include "JIT.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/System/DynamicLibrary.h"
#include "llvm/Config/config.h"
using namespace llvm;

// AtExitHandlers - List of functions to call when the program exits,
// registered with the atexit() library function.
static std::vector<void (*)()> AtExitHandlers;

/// runAtExitHandlers - Run any functions registered by the program's
/// calls to atexit(3), which we intercept and store in
/// AtExitHandlers.
///
static void runAtExitHandlers() {
  while (!AtExitHandlers.empty()) {
    void (*Fn)() = AtExitHandlers.back();
    AtExitHandlers.pop_back();
    Fn();
  }
}

//===----------------------------------------------------------------------===//
// Function stubs that are invoked instead of certain library calls
//===----------------------------------------------------------------------===//

// Force the following functions to be linked in to anything that uses the
// JIT. This is a hack designed to work around the all-too-clever Glibc
// strategy of making these functions work differently when inlined vs. when
// not inlined, and hiding their real definitions in a separate archive file
// that the dynamic linker can't see. For more info, search for
// 'libc_nonshared.a' on Google, or read http://llvm.org/PR274.
#if defined(__linux__)
#if defined(HAVE_SYS_STAT_H)
#include <sys/stat.h>
#endif
#include <fcntl.h>
/* stat functions are redirecting to __xstat with a version number.  On x86-64
 * linking with libc_nonshared.a and -Wl,--export-dynamic doesn't make 'stat'
 * available as an exported symbol, so we have to add it explicitly.
 */
namespace {
class StatSymbols {
public:
  StatSymbols() {
    sys::DynamicLibrary::AddSymbol("stat", (void*)(intptr_t)stat);
    sys::DynamicLibrary::AddSymbol("fstat", (void*)(intptr_t)fstat);
    sys::DynamicLibrary::AddSymbol("lstat", (void*)(intptr_t)lstat);
    sys::DynamicLibrary::AddSymbol("stat64", (void*)(intptr_t)stat64);
    sys::DynamicLibrary::AddSymbol("\x1stat64", (void*)(intptr_t)stat64);
    sys::DynamicLibrary::AddSymbol("\x1open64", (void*)(intptr_t)open64);
    sys::DynamicLibrary::AddSymbol("\x1lseek64", (void*)(intptr_t)lseek64);
    sys::DynamicLibrary::AddSymbol("fstat64", (void*)(intptr_t)fstat64);
    sys::DynamicLibrary::AddSymbol("lstat64", (void*)(intptr_t)lstat64);
    sys::DynamicLibrary::AddSymbol("atexit", (void*)(intptr_t)atexit);
    sys::DynamicLibrary::AddSymbol("mknod", (void*)(intptr_t)mknod);
  }
};
}
static StatSymbols initStatSymbols;
#endif // __linux__

// jit_exit - Used to intercept the "exit" library call.
static void jit_exit(int Status) {
  runAtExitHandlers();   // Run atexit handlers...
  exit(Status);
}

// jit_atexit - Used to intercept the "atexit" library call.
static int jit_atexit(void (*Fn)()) {
  AtExitHandlers.push_back(Fn);    // Take note of atexit handler...
  return 0;  // Always successful
}

static int jit_noop() {
  return 0;
}

//===----------------------------------------------------------------------===//
//
/// getPointerToNamedFunction - This method returns the address of the specified
/// function by using the dynamic loader interface.  As such it is only useful
/// for resolving library symbols, not code generated symbols.
///
void *JIT::getPointerToNamedFunction(const std::string &Name,
                                     bool AbortOnFailure) {
  if (!isSymbolSearchingDisabled()) {
    // Check to see if this is one of the functions we want to intercept.  Note,
    // we cast to intptr_t here to silence a -pedantic warning that complains
    // about casting a function pointer to a normal pointer.
    if (Name == "exit") return (void*)(intptr_t)&jit_exit;
    if (Name == "atexit") return (void*)(intptr_t)&jit_atexit;

    // We should not invoke parent's ctors/dtors from generated main()!
    // On Mingw and Cygwin, the symbol __main is resolved to
    // callee's(eg. tools/lli) one, to invoke wrong duplicated ctors
    // (and register wrong callee's dtors with atexit(3)).
    // We expect ExecutionEngine::runStaticConstructorsDestructors()
    // is called before ExecutionEngine::runFunctionAsMain() is called.
    if (Name == "__main") return (void*)(intptr_t)&jit_noop;

    const char *NameStr = Name.c_str();
    // If this is an asm specifier, skip the sentinal.
    if (NameStr[0] == 1) ++NameStr;

    // If it's an external function, look it up in the process image...
    void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr);
    if (Ptr) return Ptr;

    // If it wasn't found and if it starts with an underscore ('_') character,
    // and has an asm specifier, try again without the underscore.
    if (Name[0] == 1 && NameStr[0] == '_') {
      Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr+1);
      if (Ptr) return Ptr;
    }

    // Darwin/PPC adds $LDBLStub suffixes to various symbols like printf.  These
    // are references to hidden visibility symbols that dlsym cannot resolve.
    // If we have one of these, strip off $LDBLStub and try again.
#if defined(__APPLE__) && defined(__ppc__)
    if (Name.size() > 9 && Name[Name.size()-9] == '$' &&
        memcmp(&Name[Name.size()-8], "LDBLStub", 8) == 0) {
      // First try turning $LDBLStub into $LDBL128. If that fails, strip it off.
      // This mirrors logic in libSystemStubs.a.
      std::string Prefix = std::string(Name.begin(), Name.end()-9);
      if (void *Ptr = getPointerToNamedFunction(Prefix+"$LDBL128", false))
        return Ptr;
      if (void *Ptr = getPointerToNamedFunction(Prefix, false))
        return Ptr;
    }
#endif
  }

  /// If a LazyFunctionCreator is installed, use it to get/create the function.
  if (LazyFunctionCreator)
    if (void *RP = LazyFunctionCreator(Name))
      return RP;

  if (AbortOnFailure) {
    report_fatal_error("Program used external function '"+Name+
                      "' which could not be resolved!");
  }
  return 0;
}
<|endoftext|>


<commit_before>// Author: Jingyue

#include <cstdio>
#include <set>
using namespace std;

#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

#include "common/IDAssigner.h"
#include "common/PointerAnalysis.h"
using namespace rcs;

namespace rcs {
struct PointToDrawer: public ModulePass {
  static char ID;

  PointToDrawer(): ModulePass(ID) {}
  virtual void getAnalysisUsage(AnalysisUsage &AU) const;
  virtual bool runOnModule(Module &M);
  virtual void print(raw_ostream &O, const Module *M) const;
};
}

static cl::opt<string> DotFileName("dot",
                                   cl::desc("The output graph file name"
                                            " (.dot)"),
                                   cl::ValueRequired);

char PointToDrawer::ID = 0;

void PointToDrawer::getAnalysisUsage(AnalysisUsage &AU) const {
  AU.setPreservesAll();
  AU.addRequired<IDAssigner>();
  AU.addRequired<PointerAnalysis>();
}

bool PointToDrawer::runOnModule(Module &M) {
  IDAssigner &IDA = getAnalysis<IDAssigner>();
  PointerAnalysis &PA = getAnalysis<PointerAnalysis>();

  assert(DotFileName != "");
  string ErrorInfo;
  raw_fd_ostream DotFile(DotFileName.c_str(), ErrorInfo);
  PA.printDot(DotFile, IDA);

  return false;
}

void PointToDrawer::print(raw_ostream &O, const Module *M) const {
  // Do nothing.
}

static RegisterPass<PointToDrawer> X("draw-point-to",
                                     "Draw point-to graphs",
                                     false, // Is CFG Only?
                                     true); // Is Analysis?
<commit_msg>remove print function from PointToDrawer<commit_after>// Author: Jingyue

#include <cstdio>
#include <set>
using namespace std;

#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

#include "common/IDAssigner.h"
#include "common/PointerAnalysis.h"
using namespace rcs;

namespace rcs {
struct PointToDrawer: public ModulePass {
  static char ID;

  PointToDrawer(): ModulePass(ID) {}
  virtual void getAnalysisUsage(AnalysisUsage &AU) const;
  virtual bool runOnModule(Module &M);
};
}

static cl::opt<string> DotFileName("dot",
                                   cl::desc("The output graph file name"
                                            " (.dot)"),
                                   cl::ValueRequired);

char PointToDrawer::ID = 0;

void PointToDrawer::getAnalysisUsage(AnalysisUsage &AU) const {
  AU.setPreservesAll();
  AU.addRequired<IDAssigner>();
  AU.addRequired<PointerAnalysis>();
}

bool PointToDrawer::runOnModule(Module &M) {
  IDAssigner &IDA = getAnalysis<IDAssigner>();
  PointerAnalysis &PA = getAnalysis<PointerAnalysis>();

  assert(DotFileName != "");
  string ErrorInfo;
  raw_fd_ostream DotFile(DotFileName.c_str(), ErrorInfo);
  PA.printDot(DotFile, IDA);

  return false;
}

static RegisterPass<PointToDrawer> X("draw-point-to",
                                     "Draw point-to graphs",
                                     false, // Is CFG Only?
                                     true); // Is Analysis?
<|endoftext|>


<commit_before><commit_msg>adjust wording as in 3.8 PR<commit_after><|endoftext|>


<commit_before>//===-- GlobalStatus.cpp - Compute status info for globals -----------------==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/GlobalStatus.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Use.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/AtomicOrdering.h"
#include "llvm/Support/Casting.h"
#include <algorithm>
#include <cassert>

using namespace llvm;

/// Return the stronger of the two ordering. If the two orderings are acquire
/// and release, then return AcquireRelease.
///
static AtomicOrdering strongerOrdering(AtomicOrdering X, AtomicOrdering Y) {
  if ((X == AtomicOrdering::Acquire && Y == AtomicOrdering::Release) ||
      (Y == AtomicOrdering::Acquire && X == AtomicOrdering::Release))
    return AtomicOrdering::AcquireRelease;
  return (AtomicOrdering)std::max((unsigned)X, (unsigned)Y);
}

/// It is safe to destroy a constant iff it is only used by constants itself.
/// Note that constants cannot be cyclic, so this test is pretty easy to
/// implement recursively.
///
bool llvm::isSafeToDestroyConstant(const Constant *C) {
  if (isa<GlobalValue>(C))
    return false;

  if (isa<ConstantData>(C))
    return false;

  for (const User *U : C->users())
    if (const Constant *CU = dyn_cast<Constant>(U)) {
      if (!isSafeToDestroyConstant(CU))
        return false;
    } else
      return false;
  return true;
}

static bool analyzeGlobalAux(const Value *V, GlobalStatus &GS,
                             SmallPtrSetImpl<const PHINode *> &PhiUsers) {
  if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V))
    if (GV->isExternallyInitialized())
      GS.StoredType = GlobalStatus::StoredOnce;

  for (const Use &U : V->uses()) {
    const User *UR = U.getUser();
    if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(UR)) {
      GS.HasNonInstructionUser = true;

      // If the result of the constantexpr isn't pointer type, then we won't
      // know to expect it in various places.  Just reject early.
      if (!isa<PointerType>(CE->getType()))
        return true;

      if (analyzeGlobalAux(CE, GS, PhiUsers))
        return true;
    } else if (const Instruction *I = dyn_cast<Instruction>(UR)) {
      if (!GS.HasMultipleAccessingFunctions) {
        const Function *F = I->getParent()->getParent();
        if (!GS.AccessingFunction)
          GS.AccessingFunction = F;
        else if (GS.AccessingFunction != F)
          GS.HasMultipleAccessingFunctions = true;
      }
      if (const LoadInst *LI = dyn_cast<LoadInst>(I)) {
        GS.IsLoaded = true;
        // Don't hack on volatile loads.
        if (LI->isVolatile())
          return true;
        GS.Ordering = strongerOrdering(GS.Ordering, LI->getOrdering());
      } else if (const StoreInst *SI = dyn_cast<StoreInst>(I)) {
        // Don't allow a store OF the address, only stores TO the address.
        if (SI->getOperand(0) == V)
          return true;

        // Don't hack on volatile stores.
        if (SI->isVolatile())
          return true;

        GS.Ordering = strongerOrdering(GS.Ordering, SI->getOrdering());

        // If this is a direct store to the global (i.e., the global is a scalar
        // value, not an aggregate), keep more specific information about
        // stores.
        if (GS.StoredType != GlobalStatus::Stored) {
          if (const GlobalVariable *GV =
                  dyn_cast<GlobalVariable>(SI->getOperand(1))) {
            Value *StoredVal = SI->getOperand(0);

            if (Constant *C = dyn_cast<Constant>(StoredVal)) {
              if (C->isThreadDependent()) {
                // The stored value changes between threads; don't track it.
                return true;
              }
            }

            if (GV->hasInitializer() && StoredVal == GV->getInitializer()) {
              if (GS.StoredType < GlobalStatus::InitializerStored)
                GS.StoredType = GlobalStatus::InitializerStored;
            } else if (isa<LoadInst>(StoredVal) &&
                       cast<LoadInst>(StoredVal)->getOperand(0) == GV) {
              if (GS.StoredType < GlobalStatus::InitializerStored)
                GS.StoredType = GlobalStatus::InitializerStored;
            } else if (GS.StoredType < GlobalStatus::StoredOnce) {
              GS.StoredType = GlobalStatus::StoredOnce;
              GS.StoredOnceValue = StoredVal;
            } else if (GS.StoredType == GlobalStatus::StoredOnce &&
                       GS.StoredOnceValue == StoredVal) {
              // noop.
            } else {
              GS.StoredType = GlobalStatus::Stored;
            }
          } else {
            GS.StoredType = GlobalStatus::Stored;
          }
        }
      } else if (isa<BitCastInst>(I)) {
        if (analyzeGlobalAux(I, GS, PhiUsers))
          return true;
      } else if (isa<GetElementPtrInst>(I)) {
        if (analyzeGlobalAux(I, GS, PhiUsers))
          return true;
      } else if (isa<SelectInst>(I)) {
        if (analyzeGlobalAux(I, GS, PhiUsers))
          return true;
      } else if (const PHINode *PN = dyn_cast<PHINode>(I)) {
        // PHI nodes we can check just like select or GEP instructions, but we
        // have to be careful about infinite recursion.
        if (PhiUsers.insert(PN).second) // Not already visited.
          if (analyzeGlobalAux(I, GS, PhiUsers))
            return true;
      } else if (isa<CmpInst>(I)) {
        GS.IsCompared = true;
      } else if (const MemTransferInst *MTI = dyn_cast<MemTransferInst>(I)) {
        if (MTI->isVolatile())
          return true;
        if (MTI->getArgOperand(0) == V)
          GS.StoredType = GlobalStatus::Stored;
        if (MTI->getArgOperand(1) == V)
          GS.IsLoaded = true;
      } else if (const MemSetInst *MSI = dyn_cast<MemSetInst>(I)) {
        assert(MSI->getArgOperand(0) == V && "Memset only takes one pointer!");
        if (MSI->isVolatile())
          return true;
        GS.StoredType = GlobalStatus::Stored;
      } else if (auto C = ImmutableCallSite(I)) {
        if (!C.isCallee(&U))
          return true;
        GS.IsLoaded = true;
      } else {
        return true; // Any other non-load instruction might take address!
      }
    } else if (const Constant *C = dyn_cast<Constant>(UR)) {
      GS.HasNonInstructionUser = true;
      // We might have a dead and dangling constant hanging off of here.
      if (!isSafeToDestroyConstant(C))
        return true;
    } else {
      GS.HasNonInstructionUser = true;
      // Otherwise must be some other user.
      return true;
    }
  }

  return false;
}

GlobalStatus::GlobalStatus() = default;

bool GlobalStatus::analyzeGlobal(const Value *V, GlobalStatus &GS) {
  SmallPtrSet<const PHINode *, 16> PhiUsers;
  return analyzeGlobalAux(V, GS, PhiUsers);
}
<commit_msg>[GlobalOpt] Fix exponential compile-time with selects.<commit_after>//===-- GlobalStatus.cpp - Compute status info for globals -----------------==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/GlobalStatus.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Use.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/AtomicOrdering.h"
#include "llvm/Support/Casting.h"
#include <algorithm>
#include <cassert>

using namespace llvm;

/// Return the stronger of the two ordering. If the two orderings are acquire
/// and release, then return AcquireRelease.
///
static AtomicOrdering strongerOrdering(AtomicOrdering X, AtomicOrdering Y) {
  if ((X == AtomicOrdering::Acquire && Y == AtomicOrdering::Release) ||
      (Y == AtomicOrdering::Acquire && X == AtomicOrdering::Release))
    return AtomicOrdering::AcquireRelease;
  return (AtomicOrdering)std::max((unsigned)X, (unsigned)Y);
}

/// It is safe to destroy a constant iff it is only used by constants itself.
/// Note that constants cannot be cyclic, so this test is pretty easy to
/// implement recursively.
///
bool llvm::isSafeToDestroyConstant(const Constant *C) {
  if (isa<GlobalValue>(C))
    return false;

  if (isa<ConstantData>(C))
    return false;

  for (const User *U : C->users())
    if (const Constant *CU = dyn_cast<Constant>(U)) {
      if (!isSafeToDestroyConstant(CU))
        return false;
    } else
      return false;
  return true;
}

static bool analyzeGlobalAux(const Value *V, GlobalStatus &GS,
                             SmallPtrSetImpl<const Value *> &VisitedUsers) {
  if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V))
    if (GV->isExternallyInitialized())
      GS.StoredType = GlobalStatus::StoredOnce;

  for (const Use &U : V->uses()) {
    const User *UR = U.getUser();
    if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(UR)) {
      GS.HasNonInstructionUser = true;

      // If the result of the constantexpr isn't pointer type, then we won't
      // know to expect it in various places.  Just reject early.
      if (!isa<PointerType>(CE->getType()))
        return true;

      // FIXME: Do we need to add constexpr selects to VisitedUsers?
      if (analyzeGlobalAux(CE, GS, VisitedUsers))
        return true;
    } else if (const Instruction *I = dyn_cast<Instruction>(UR)) {
      if (!GS.HasMultipleAccessingFunctions) {
        const Function *F = I->getParent()->getParent();
        if (!GS.AccessingFunction)
          GS.AccessingFunction = F;
        else if (GS.AccessingFunction != F)
          GS.HasMultipleAccessingFunctions = true;
      }
      if (const LoadInst *LI = dyn_cast<LoadInst>(I)) {
        GS.IsLoaded = true;
        // Don't hack on volatile loads.
        if (LI->isVolatile())
          return true;
        GS.Ordering = strongerOrdering(GS.Ordering, LI->getOrdering());
      } else if (const StoreInst *SI = dyn_cast<StoreInst>(I)) {
        // Don't allow a store OF the address, only stores TO the address.
        if (SI->getOperand(0) == V)
          return true;

        // Don't hack on volatile stores.
        if (SI->isVolatile())
          return true;

        GS.Ordering = strongerOrdering(GS.Ordering, SI->getOrdering());

        // If this is a direct store to the global (i.e., the global is a scalar
        // value, not an aggregate), keep more specific information about
        // stores.
        if (GS.StoredType != GlobalStatus::Stored) {
          if (const GlobalVariable *GV =
                  dyn_cast<GlobalVariable>(SI->getOperand(1))) {
            Value *StoredVal = SI->getOperand(0);

            if (Constant *C = dyn_cast<Constant>(StoredVal)) {
              if (C->isThreadDependent()) {
                // The stored value changes between threads; don't track it.
                return true;
              }
            }

            if (GV->hasInitializer() && StoredVal == GV->getInitializer()) {
              if (GS.StoredType < GlobalStatus::InitializerStored)
                GS.StoredType = GlobalStatus::InitializerStored;
            } else if (isa<LoadInst>(StoredVal) &&
                       cast<LoadInst>(StoredVal)->getOperand(0) == GV) {
              if (GS.StoredType < GlobalStatus::InitializerStored)
                GS.StoredType = GlobalStatus::InitializerStored;
            } else if (GS.StoredType < GlobalStatus::StoredOnce) {
              GS.StoredType = GlobalStatus::StoredOnce;
              GS.StoredOnceValue = StoredVal;
            } else if (GS.StoredType == GlobalStatus::StoredOnce &&
                       GS.StoredOnceValue == StoredVal) {
              // noop.
            } else {
              GS.StoredType = GlobalStatus::Stored;
            }
          } else {
            GS.StoredType = GlobalStatus::Stored;
          }
        }
      } else if (isa<BitCastInst>(I) || isa<GetElementPtrInst>(I)) {
        // Skip over bitcasts and GEPs; we don't care about the type or offset
        // of the pointer.
        if (analyzeGlobalAux(I, GS, VisitedUsers))
          return true;
      } else if (isa<SelectInst>(I) || isa<PHINode>(I)) {
        // Look through selects and PHIs to find if the pointer is
        // conditionally accessed. Make sure we only visit an instruction
        // once; otherwise, we can get infinite recursion or exponential
        // compile time.
        if (VisitedUsers.insert(I).second)
          if (analyzeGlobalAux(I, GS, VisitedUsers))
            return true;
      } else if (isa<CmpInst>(I)) {
        GS.IsCompared = true;
      } else if (const MemTransferInst *MTI = dyn_cast<MemTransferInst>(I)) {
        if (MTI->isVolatile())
          return true;
        if (MTI->getArgOperand(0) == V)
          GS.StoredType = GlobalStatus::Stored;
        if (MTI->getArgOperand(1) == V)
          GS.IsLoaded = true;
      } else if (const MemSetInst *MSI = dyn_cast<MemSetInst>(I)) {
        assert(MSI->getArgOperand(0) == V && "Memset only takes one pointer!");
        if (MSI->isVolatile())
          return true;
        GS.StoredType = GlobalStatus::Stored;
      } else if (auto C = ImmutableCallSite(I)) {
        if (!C.isCallee(&U))
          return true;
        GS.IsLoaded = true;
      } else {
        return true; // Any other non-load instruction might take address!
      }
    } else if (const Constant *C = dyn_cast<Constant>(UR)) {
      GS.HasNonInstructionUser = true;
      // We might have a dead and dangling constant hanging off of here.
      if (!isSafeToDestroyConstant(C))
        return true;
    } else {
      GS.HasNonInstructionUser = true;
      // Otherwise must be some other user.
      return true;
    }
  }

  return false;
}

GlobalStatus::GlobalStatus() = default;

bool GlobalStatus::analyzeGlobal(const Value *V, GlobalStatus &GS) {
  SmallPtrSet<const Value *, 16> VisitedUsers;
  return analyzeGlobalAux(V, GS, VisitedUsers);
}
<|endoftext|>


<commit_before>/*
 Flexisip, a flexible SIP proxy server with media capabilities.
 Copyright (C) 2012  Belledonne Communications SARL.

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.

 This program 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 Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "forkcontext.hh"
#include "registrardb.hh"

using namespace ::std;

ForkContextConfig::ForkContextConfig() : mDeliveryTimeout(0),mUrgentTimeout(5),
	mForkLate(false),mForkOneResponse(false), mForkNoGlobalDecline(false),
	mTreatDeclineAsUrgent(false), mRemoveToTag(false){
}

void ForkContext::__timer_callback(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg){
	(static_cast<ForkContext*>(arg))->onLateTimeout();
}

ForkContext::ForkContext(Agent *agent, const std::shared_ptr<RequestSipEvent> &event, shared_ptr<ForkContextConfig> cfg, ForkContextListener* listener) :
		mListener(listener), mAgent(agent), mEvent(make_shared<RequestSipEvent>(event)), mCfg(cfg), mLateTimer(NULL) {
	mLateTimerExpired=false;
	su_home_init(&mHome);
}

void ForkContext::checkFinished(){
	bool finished=false;
	if (!mIncoming && mOutgoings.empty()){
		if (mLateTimer){
			finished=mLateTimerExpired;
		}else finished=true;
	}
	if (finished)
		setFinished();
}

void ForkContext::onLateTimeout(){
	LOGD("ForkContext: late timer expired.");
	shared_ptr<ForkContext> me(shared_from_this()); //this is to keep the object alive at least the time of the timer callback.
	//Indeed sofia does not hold a refcount to the ForkContext. During checkFinished() the refcount my drop to zero, but we need to the object
	//to be kept alive until checkFinished() returns.
	mLateTimerExpired=true;
	checkFinished();
}

struct dest_finder{
	dest_finder(const url_t *ctt) {
		cttport = url_port(ctt);
		ctthost = ctt->url_host;
		// don't care about transport
	}
	bool operator()(const url_t *dest){
		return 0 == strcmp(url_port(dest), cttport)
			&& 0 == strcmp(dest->url_host, ctthost);
	}
	const char *ctthost;
	const char *cttport;
};

//this implementation looks for already pending or failed transactions and then rejects handling of a new one that would already been tried.
bool ForkContext::onNewRegister(const sip_contact_t* ctt){
	auto it=find_if(mDestinationUris.begin(),mDestinationUris.end(),dest_finder(ctt->m_url));
	if (it!=mDestinationUris.end()){
		LOGD("ForkContext %p: onNewRegister(): destination already handled.",this);
		return false;
	}
	return true;
}

void ForkContext::onNew(const shared_ptr<IncomingTransaction> &transaction) {
	mIncoming = transaction;
	if (mCfg->mForkLate && mLateTimer==NULL){
		/*this timer is for when outgoing transaction all die prematuraly, we still need to wait that late register arrive.*/
		mLateTimer=su_timer_create(su_root_task(mAgent->getRoot()), 0);
		su_timer_set_interval(mLateTimer, &ForkContext::__timer_callback, this, (su_duration_t)mCfg->mDeliveryTimeout* (su_duration_t)1000);
	}
}

void ForkContext::onDestroy(const shared_ptr<IncomingTransaction> &transaction) {
	mIncoming.reset();
	checkFinished();
}

void ForkContext::onNew(const shared_ptr<OutgoingTransaction> &transaction) {
	mOutgoings.push_back(transaction);
	mDestinationUris.push_back(url_hdup(&mHome,transaction->getRequestUri()));
}

void ForkContext::onDestroy(const shared_ptr<OutgoingTransaction> &transaction) {
	mOutgoings.remove(transaction);
	checkFinished();
}

const shared_ptr<RequestSipEvent> &ForkContext::getEvent() {
	return mEvent;
}

ForkContext::~ForkContext() {
	if (mLateTimer)
		su_timer_destroy(mLateTimer);
	su_home_deinit(&mHome);
}

void ForkContext::setFinished(){
	if (mLateTimer){
		su_timer_destroy(mLateTimer);
		mLateTimer=NULL;
	}
	//force reference to be loosed immediately, to avoid circular dependencies.
	mEvent.reset();
	mIncoming.reset();
	mOutgoings.clear();
	mListener->onForkContextFinished(shared_from_this());
}

<commit_msg>Initialize properly forkcontext.<commit_after>/*
 Flexisip, a flexible SIP proxy server with media capabilities.
 Copyright (C) 2012  Belledonne Communications SARL.

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as
 published by the Free Software Foundation, either version 3 of the
 License, or (at your option) any later version.

 This program 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 Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "forkcontext.hh"
#include "registrardb.hh"

using namespace ::std;

ForkContextConfig::ForkContextConfig() : mDeliveryTimeout(0),mUrgentTimeout(5),
	mForkLate(false),mForkOneResponse(false), mForkNoGlobalDecline(false),
	mTreatDeclineAsUrgent(false), mRemoveToTag(false){
}

void ForkContext::__timer_callback(su_root_magic_t *magic, su_timer_t *t, su_timer_arg_t *arg){
	(static_cast<ForkContext*>(arg))->onLateTimeout();
}

ForkContext::ForkContext(Agent *agent, const std::shared_ptr<RequestSipEvent> &event, shared_ptr<ForkContextConfig> cfg, ForkContextListener* listener) :
		mListener(listener),
		mAgent(agent),
		mEvent(make_shared<RequestSipEvent>(event)),
		mIncoming(),
		mOutgoings(),
		mDestinationUris(),
		mCfg(cfg),
		mLateTimer(NULL),
		mLateTimerExpired(false) {
	su_home_init(&mHome);
}

void ForkContext::checkFinished(){
	bool finished=false;
	if (!mIncoming && mOutgoings.empty()){
		if (mLateTimer){
			finished=mLateTimerExpired;
		}else finished=true;
	}
	if (finished)
		setFinished();
}

void ForkContext::onLateTimeout(){
	LOGD("ForkContext: late timer expired.");
	shared_ptr<ForkContext> me(shared_from_this()); //this is to keep the object alive at least the time of the timer callback.
	//Indeed sofia does not hold a refcount to the ForkContext. During checkFinished() the refcount my drop to zero, but we need to the object
	//to be kept alive until checkFinished() returns.
	mLateTimerExpired=true;
	checkFinished();
}

struct dest_finder{
	dest_finder(const url_t *ctt) {
		cttport = url_port(ctt);
		ctthost = ctt->url_host;
		// don't care about transport
	}
	bool operator()(const url_t *dest){
		return 0 == strcmp(url_port(dest), cttport)
			&& 0 == strcmp(dest->url_host, ctthost);
	}
	const char *ctthost;
	const char *cttport;
};

//this implementation looks for already pending or failed transactions and then rejects handling of a new one that would already been tried.
bool ForkContext::onNewRegister(const sip_contact_t* ctt){
	auto it=find_if(mDestinationUris.begin(),mDestinationUris.end(),dest_finder(ctt->m_url));
	if (it!=mDestinationUris.end()){
		LOGD("ForkContext %p: onNewRegister(): destination already handled.",this);
		return false;
	}
	return true;
}

void ForkContext::onNew(const shared_ptr<IncomingTransaction> &transaction) {
	mIncoming = transaction;
	if (mCfg->mForkLate && mLateTimer==NULL){
		/*this timer is for when outgoing transaction all die prematuraly, we still need to wait that late register arrive.*/
		mLateTimer=su_timer_create(su_root_task(mAgent->getRoot()), 0);
		su_timer_set_interval(mLateTimer, &ForkContext::__timer_callback, this, (su_duration_t)mCfg->mDeliveryTimeout* (su_duration_t)1000);
	}
}

void ForkContext::onDestroy(const shared_ptr<IncomingTransaction> &transaction) {
	mIncoming.reset();
	checkFinished();
}

void ForkContext::onNew(const shared_ptr<OutgoingTransaction> &transaction) {
	mOutgoings.push_back(transaction);
	mDestinationUris.push_back(url_hdup(&mHome,transaction->getRequestUri()));
}

void ForkContext::onDestroy(const shared_ptr<OutgoingTransaction> &transaction) {
	mOutgoings.remove(transaction);
	checkFinished();
}

const shared_ptr<RequestSipEvent> &ForkContext::getEvent() {
	return mEvent;
}

ForkContext::~ForkContext() {
	if (mLateTimer)
		su_timer_destroy(mLateTimer);
	su_home_deinit(&mHome);
}

void ForkContext::setFinished(){
	if (mLateTimer){
		su_timer_destroy(mLateTimer);
		mLateTimer=NULL;
	}
	//force reference to be loosed immediately, to avoid circular dependencies.
	mEvent.reset();
	mIncoming.reset();
	mOutgoings.clear();
	mListener->onForkContextFinished(shared_from_this());
}

<|endoftext|>