<commit_before>#include "Overlay.h"
#include "ui_Overlay.h"

#include "../Hearthstone.h"
#include "../Settings.h"

#ifdef Q_OS_MAC
#include <objc/objc-runtime.h>
#endif

#include <cassert>

#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QFile>
#include <QMouseEvent>

#define CHECK_FOR_OVERLAY_HOVER_INTERVAL_MS 100

class OverlayHistoryWindow {
private:
  QString mTitle;
  OverlayHistoryList mHistory;

  QFont mRowFont;
  QFont mTitleFont;

  int mWidth;

  int mPadding;
  int mRowSpacing;

  int TitleHeight() const {
    QFontMetrics titleMetrics( mTitleFont );
    return titleMetrics.ascent() - titleMetrics.descent();
  }

  int Padding() const {
    return mPadding;
  }

  int RowSpacing() const {
    return mRowSpacing;
  }

  int RowHeight() const {
    QFontMetrics rowMetrics( mRowFont );
    return rowMetrics.ascent() - rowMetrics.descent();
  }

  int RowWidth() const {
    return Width() - Padding() * 2;
  }

  void DrawMana( QPainter& painter, int x, int y, int width, int height, int mana ) const {
    // Draw mana
    QPen origPen = painter.pen();
    QPen pen( QColor( 0, 52, 113 ) );
    pen.setCosmetic( true );
    pen.setWidth( 1 );
    painter.setPen( pen );

    QBrush brush( QColor( 40, 119, 238 ) );
    painter.setBrush( brush );

    QTransform transform;
    painter.translate( x + width * 0.5, y + height * 0.5 );
    painter.scale( width * 0.8, height * 0.8 );

    static const QPointF points[5] = {
      QPointF( 0.0, -1.0 ),
      QPointF( 1.0, -0.2 ),
      QPointF( 0.6, 1.0 ),
      QPointF( -0.6, 1.0 ),
      QPointF( -1.0, -0.2 ),
    };
    painter.drawConvexPolygon( points, 5 );
    painter.resetTransform();
    painter.setPen( origPen );

    painter.drawText( x, y, width, height, Qt::AlignCenter | Qt::AlignVCenter, QString::number( mana ) );
  }

  void DrawCardLine( QPainter& painter, int x, int y, int width, int height, const QString& name, int count ) const {
    painter.save();
    painter.drawText( x, y, width, height, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, name );

    if( count > 1 ) {
      int nameWidth = QFontMetrics( painter.font() ).width( name + " " );
      QString countString = QString( "x%1" ).arg( count );
      QFont font = painter.font();
      font.setBold( true );
      painter.setFont( font );
      painter.drawText( x + nameWidth, y, width - nameWidth, height, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, countString );
    }
    painter.restore();
  }

public:
  OverlayHistoryWindow( const QString& title, const OverlayHistoryList& history, int width, int padding, int rowSpacing, float titleFontSize, float rowFontSize )
    : mTitle( title ), mHistory( history ), mWidth( width ), mPadding( padding ), mRowSpacing( rowSpacing )
  {
    mRowFont.setPointSize( titleFontSize );
    mTitleFont.setPointSize( rowFontSize );
    mTitleFont.setUnderline( true );
    mTitleFont.setBold( true );
  }

  int Width() const {
    return mWidth;
  }

  int Height() const {
    return ( mHistory.count() - 1 ) * RowSpacing() + // Spacing between items
      mHistory.count() * RowHeight() + // Height per item
      TitleHeight() + RowSpacing() + // Title
      Padding() * 2; // Overall padding
  }

  void Paint( QPainter& painter, int x, int y ) const {
    painter.save();

    QRect rect( x, y, Width(), Height() );
    painter.setClipRect( rect );

    // BG
    QPen pen = QPen( QColor( 160, 160, 160 ) );
    pen.setWidth( 3 );
    painter.setPen( pen );
    painter.setBrush( QBrush( QColor( 70, 70, 70, 175 ) ) );
    painter.drawRoundedRect( rect, 10, 10 );

    // Title
    y += Padding();
    painter.setPen( QPen( Qt::white) );
    painter.setFont( mTitleFont );
    painter.drawText( x, y, Width(), TitleHeight(), Qt::AlignCenter | Qt::AlignVCenter | Qt::TextDontClip, mTitle );
    y += TitleHeight() + RowSpacing();

    // Lines
    painter.setPen( QPen( Qt::white) );
    painter.setFont( mRowFont );
    for( const QVariantMap& it : mHistory ) {
      int mx = x + Padding();
      DrawMana( painter, mx, y, RowHeight(), RowHeight(), it["mana"].toInt() );
      int cx = mx + RowHeight() + 5;
      DrawCardLine( painter, cx, y, RowWidth() - cx, RowHeight(), it["name"].toString(), it["count"].toInt() );
      y += RowHeight();
      y += RowSpacing();
    }

    painter.restore();
  }

};

Overlay::Overlay( QWidget *parent )
  : QMainWindow( parent ), mUI( new Ui::Overlay ), mShowPlayerHistory( PLAYER_UNKNOWN )
{
  mUI->setupUi( this );
  setWindowFlags( Qt::NoDropShadowWindowHint | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowTransparentForInput );

#ifdef Q_OS_WIN
  setWindowFlags( windowFlags() | Qt::Tool );
#else
  setWindowFlags( windowFlags() | Qt::Window );
#endif

  setAttribute( Qt::WA_TranslucentBackground );
  setAttribute( Qt::WA_ShowWithoutActivating );

  connect( Hearthstone::Instance(), &Hearthstone::GameWindowChanged, this, &Overlay::HandleGameWindowChanged );
  connect( Hearthstone::Instance(), &Hearthstone::GameStarted, this, &Overlay::HandleGameStarted );
  connect( Hearthstone::Instance(), &Hearthstone::GameStopped, this, &Overlay::HandleGameStopped );

  connect( &mCheckForHoverTimer, &QTimer::timeout, this, &Overlay::CheckForHover );

  connect( Settings::Instance(), &Settings::OverlayEnabledChanged, this, &Overlay::HandleOverlaySettingChanged );

  LoadCards();

  hide();

#ifdef Q_OS_MAC
  WId windowObject = this->winId();
  objc_object* nsviewObject = reinterpret_cast<objc_object*>(windowObject);
  objc_object* nsWindowObject = objc_msgSend( nsviewObject, sel_registerName("window") );
  int NSWindowCollectionBehaviorCanJoinAllSpaces = 1 << 0;
  objc_msgSend( nsWindowObject, sel_registerName("setCollectionBehavior:"), NSWindowCollectionBehaviorCanJoinAllSpaces );

  // Ignore mouse events on Mac
  // Qt::WindowTransparentForInput bug
  // https://bugreports.qt.io/browse/QTBUG-45498
  objc_msgSend( nsWindowObject, sel_registerName("setIgnoresMouseEvents:"), 1 );
#endif
}

Overlay::~Overlay() {
  delete mUI;
}

void Overlay::CheckForHover() {
  QPoint mouseLoc = mapFromGlobal( QCursor::pos() );

  Player showPlayerHistory = PLAYER_UNKNOWN;

  if( mPlayerDeckRect.contains( mouseLoc ) ) {
    showPlayerHistory = PLAYER_SELF;
  } else if( mOpponentDeckRect.contains( mouseLoc ) ) {
    showPlayerHistory = PLAYER_OPPONENT;
  }

  if( mShowPlayerHistory != showPlayerHistory ) {
    mShowPlayerHistory = showPlayerHistory;
    update();
  }
}

void Overlay::LoadCards() {
  QFile file( ":/data/cards.json" );
  bool opened = file.open( QIODevice::ReadOnly | QIODevice::Text );
  assert( opened );

  QByteArray jsonData = file.readAll();
  QJsonParseError error;
  QJsonArray jsonCards = QJsonDocument::fromJson( jsonData, &error ).array();
  assert( error.error == QJsonParseError::NoError );

  mCardDB.clear();

  for( QJsonValueRef jsonCardRef : jsonCards ) {
    QJsonObject jsonCard = jsonCardRef.toObject();
    mCardDB[ jsonCard["id"].toString() ] = jsonCard.toVariantMap();
  }
}

void PaintHistoryInScreen( QPainter& painter, const OverlayHistoryWindow& wnd, const QPoint& pos ) {
  int padding = 10;

  QRect rect( pos.x() + 20, pos.y(), wnd.Width(), wnd.Height() );
  rect.translate( -qMax( rect.right() - painter.device()->width() + padding, 0 ), -qMax( rect.bottom() - painter.device()->height() + padding, 0 ) ); // fit to window
  wnd.Paint( painter, rect.x(), rect.y() );
}

void Overlay::paintEvent( QPaintEvent* ) {
  QString title;
  QRect rect;
  OverlayHistoryList *history = NULL;

  if( mShowPlayerHistory == PLAYER_SELF && mPlayerHistory.count() > 0 ) {
    title = "Cards drawn";
    history = &mPlayerHistory;
    rect = mPlayerDeckRect;
  } else if( mShowPlayerHistory == PLAYER_OPPONENT && mOpponentHistory.count() > 0 ) {
    title = "Cards played by opponent";
    history = &mOpponentHistory;
    rect = mOpponentDeckRect;
  }

  QPainter painter( this );
  painter.setRenderHint( QPainter::Antialiasing );

#ifdef Q_OS_WIN
  float rowFontSize = 9;
  float titleFontSize = 9;
#else
  float rowFontSize = 12;
  float titleFontSize = 12;
#endif

  int spacing = 8;
  int overlayWidth = 200;

  if( history ) {
    OverlayHistoryWindow wnd( title, *history, overlayWidth, spacing, spacing, titleFontSize, rowFontSize );
    QPoint pos = rect.center() + QPoint( rect.width() / 2 + 10, -wnd.Height() / 2 );
    PaintHistoryInScreen( painter, wnd, pos );
  }
}

void Overlay::HandleGameWindowChanged( int x, int y, int w, int h ) {
  move( x, y );
  setFixedSize( w, h );

  int minWidth = h * 4 / 3;
  mPlayerDeckRect = QRect( w / 2 + 0.440 * minWidth, h * 0.510, 0.05 * minWidth, h * 0.170 );
  mOpponentDeckRect = mPlayerDeckRect.translated( -0.005 * minWidth, -0.275 * h );

  update();
}

void Overlay::Update() {
  if( Hearthstone::Instance()->GameRunning() && Settings::Instance()->OverlayEnabled() ) {
    show();
#ifdef Q_OS_WIN
    setAttribute( Qt::WA_QuitOnClose ); // otherwise taskkill /IM Track-o-Bot.exe does not work (http://www.qtcentre.org/threads/11713-Qt-Tool?p=62466#post62466)
#endif
  } else {
    hide();
  }

  update();
}

void Overlay::HandleGameStarted() {
  mCheckForHoverTimer.start( CHECK_FOR_OVERLAY_HOVER_INTERVAL_MS );
  Update();
}

void Overlay::HandleGameStopped() {
  mCheckForHoverTimer.stop();
  Update();
}

void Overlay::UpdateHistoryFor( Player player, const ::CardHistoryList& list ) {
  QMap< QString, QVariantMap > entries;

  for( const CardHistoryItem& it : list ) {
    const QString& cardId = it.cardId;

    if( cardId.isEmpty() ) {
      continue;
    }

    if( !mCardDB.contains( cardId ) ) {
      DBG( "Card %s not found", qt2cstr( cardId ) );
      continue;
    }

    if( mCardDB[ cardId ][ "type" ] == "hero" ) {
      continue;
    }

    if( it.player != player ) {
      continue;
    }

    QVariantMap& entry = entries[ cardId ];
    entry[ "count" ] = entry.value( "count", 0 ).toInt() + 1;
    entry[ "mana" ] = mCardDB[ cardId ][ "mana" ];
    entry[ "name" ] = mCardDB[ cardId ][ "name" ];
  }

  OverlayHistoryList* ref;
  if( player == PLAYER_SELF ) {
    ref = &mPlayerHistory;
  } else {
    ref = &mOpponentHistory;
  }

  *ref = entries.values();

  qSort( ref->begin(), ref->end(), []( const QVariantMap& a, const QVariantMap& b ) {
    if( a["mana"].toInt() == b["mana"].toInt() ) {
      return a["name"].toString() < b["name"].toString();
    } else {
      return a["mana"].toInt() < b["mana"].toInt();
    }
  });
}

void Overlay::HandleCardsDrawnUpdate( const ::CardHistoryList& cardsDrawn ) {
  UpdateHistoryFor( PLAYER_OPPONENT, cardsDrawn );
  UpdateHistoryFor( PLAYER_SELF, cardsDrawn );
  Update();
}

void Overlay::HandleOverlaySettingChanged( bool enabled ) {
  UNUSED_ARG( enabled );

  Update();
}
<commit_msg>Fix fullscreen issue w/ overlay<commit_after>#include "Overlay.h"
#include "ui_Overlay.h"

#include "../Hearthstone.h"
#include "../Settings.h"

#ifdef Q_OS_MAC
#include <objc/objc-runtime.h>
#endif

#include <cassert>

#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QFile>
#include <QMouseEvent>

#define CHECK_FOR_OVERLAY_HOVER_INTERVAL_MS 100

class OverlayHistoryWindow {
private:
  QString mTitle;
  OverlayHistoryList mHistory;

  QFont mRowFont;
  QFont mTitleFont;

  int mWidth;

  int mPadding;
  int mRowSpacing;

  int TitleHeight() const {
    QFontMetrics titleMetrics( mTitleFont );
    return titleMetrics.ascent() - titleMetrics.descent();
  }

  int Padding() const {
    return mPadding;
  }

  int RowSpacing() const {
    return mRowSpacing;
  }

  int RowHeight() const {
    QFontMetrics rowMetrics( mRowFont );
    return rowMetrics.ascent() - rowMetrics.descent();
  }

  int RowWidth() const {
    return Width() - Padding() * 2;
  }

  void DrawMana( QPainter& painter, int x, int y, int width, int height, int mana ) const {
    // Draw mana
    QPen origPen = painter.pen();
    QPen pen( QColor( 0, 52, 113 ) );
    pen.setCosmetic( true );
    pen.setWidth( 1 );
    painter.setPen( pen );

    QBrush brush( QColor( 40, 119, 238 ) );
    painter.setBrush( brush );

    QTransform transform;
    painter.translate( x + width * 0.5, y + height * 0.5 );
    painter.scale( width * 0.8, height * 0.8 );

    static const QPointF points[5] = {
      QPointF( 0.0, -1.0 ),
      QPointF( 1.0, -0.2 ),
      QPointF( 0.6, 1.0 ),
      QPointF( -0.6, 1.0 ),
      QPointF( -1.0, -0.2 ),
    };
    painter.drawConvexPolygon( points, 5 );
    painter.resetTransform();
    painter.setPen( origPen );

    painter.drawText( x, y, width, height, Qt::AlignCenter | Qt::AlignVCenter, QString::number( mana ) );
  }

  void DrawCardLine( QPainter& painter, int x, int y, int width, int height, const QString& name, int count ) const {
    painter.save();
    painter.drawText( x, y, width, height, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, name );

    if( count > 1 ) {
      int nameWidth = QFontMetrics( painter.font() ).width( name + " " );
      QString countString = QString( "x%1" ).arg( count );
      QFont font = painter.font();
      font.setBold( true );
      painter.setFont( font );
      painter.drawText( x + nameWidth, y, width - nameWidth, height, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, countString );
    }
    painter.restore();
  }

public:
  OverlayHistoryWindow( const QString& title, const OverlayHistoryList& history, int width, int padding, int rowSpacing, float titleFontSize, float rowFontSize )
    : mTitle( title ), mHistory( history ), mWidth( width ), mPadding( padding ), mRowSpacing( rowSpacing )
  {
    mRowFont.setPointSize( titleFontSize );
    mTitleFont.setPointSize( rowFontSize );
    mTitleFont.setUnderline( true );
    mTitleFont.setBold( true );
  }

  int Width() const {
    return mWidth;
  }

  int Height() const {
    return ( mHistory.count() - 1 ) * RowSpacing() + // Spacing between items
      mHistory.count() * RowHeight() + // Height per item
      TitleHeight() + RowSpacing() + // Title
      Padding() * 2; // Overall padding
  }

  void Paint( QPainter& painter, int x, int y ) const {
    painter.save();

    QRect rect( x, y, Width(), Height() );
    painter.setClipRect( rect );

    // BG
    QPen pen = QPen( QColor( 160, 160, 160 ) );
    pen.setWidth( 3 );
    painter.setPen( pen );
    painter.setBrush( QBrush( QColor( 70, 70, 70, 175 ) ) );
    painter.drawRoundedRect( rect, 10, 10 );

    // Title
    y += Padding();
    painter.setPen( QPen( Qt::white) );
    painter.setFont( mTitleFont );
    painter.drawText( x, y, Width(), TitleHeight(), Qt::AlignCenter | Qt::AlignVCenter | Qt::TextDontClip, mTitle );
    y += TitleHeight() + RowSpacing();

    // Lines
    painter.setPen( QPen( Qt::white) );
    painter.setFont( mRowFont );
    for( const QVariantMap& it : mHistory ) {
      int mx = x + Padding();
      DrawMana( painter, mx, y, RowHeight(), RowHeight(), it["mana"].toInt() );
      int cx = mx + RowHeight() + 5;
      DrawCardLine( painter, cx, y, RowWidth() - cx, RowHeight(), it["name"].toString(), it["count"].toInt() );
      y += RowHeight();
      y += RowSpacing();
    }

    painter.restore();
  }

};

Overlay::Overlay( QWidget *parent )
  : QMainWindow( parent ), mUI( new Ui::Overlay ), mShowPlayerHistory( PLAYER_UNKNOWN )
{
  mUI->setupUi( this );
  setWindowFlags( Qt::NoDropShadowWindowHint | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::WindowTransparentForInput );

#ifdef Q_OS_WIN
  setWindowFlags( windowFlags() | Qt::Tool );
#else
  setWindowFlags( windowFlags() | Qt::Window );
#endif

  setAttribute( Qt::WA_TranslucentBackground );
  setAttribute( Qt::WA_ShowWithoutActivating );

  connect( Hearthstone::Instance(), &Hearthstone::GameWindowChanged, this, &Overlay::HandleGameWindowChanged );
  connect( Hearthstone::Instance(), &Hearthstone::GameStarted, this, &Overlay::HandleGameStarted );
  connect( Hearthstone::Instance(), &Hearthstone::GameStopped, this, &Overlay::HandleGameStopped );

  connect( &mCheckForHoverTimer, &QTimer::timeout, this, &Overlay::CheckForHover );

  connect( Settings::Instance(), &Settings::OverlayEnabledChanged, this, &Overlay::HandleOverlaySettingChanged );

  LoadCards();

  hide();

#ifdef Q_OS_MAC
  WId windowObject = this->winId();
  objc_object* nsviewObject = reinterpret_cast<objc_object*>(windowObject);
  objc_object* nsWindowObject = objc_msgSend( nsviewObject, sel_registerName("window") );
  int NSWindowCollectionBehaviorCanJoinAllSpaces = 1 << 0;
  objc_msgSend( nsWindowObject, sel_registerName("setCollectionBehavior:"), NSWindowCollectionBehaviorCanJoinAllSpaces );

  // Ignore mouse events on Mac
  // Qt::WindowTransparentForInput bug
  // https://bugreports.qt.io/browse/QTBUG-45498
  objc_msgSend( nsWindowObject, sel_registerName("setIgnoresMouseEvents:"), 1 );
#endif
}

Overlay::~Overlay() {
  delete mUI;
}

void Overlay::CheckForHover() {
  QPoint mouseLoc = mapFromGlobal( QCursor::pos() );

  Player showPlayerHistory = PLAYER_UNKNOWN;

  if( mPlayerDeckRect.contains( mouseLoc ) ) {
    showPlayerHistory = PLAYER_SELF;
  } else if( mOpponentDeckRect.contains( mouseLoc ) ) {
    showPlayerHistory = PLAYER_OPPONENT;
  }

  if( mShowPlayerHistory != showPlayerHistory ) {
    mShowPlayerHistory = showPlayerHistory;
    update();
  }
}

void Overlay::LoadCards() {
  QFile file( ":/data/cards.json" );
  bool opened = file.open( QIODevice::ReadOnly | QIODevice::Text );
  assert( opened );

  QByteArray jsonData = file.readAll();
  QJsonParseError error;
  QJsonArray jsonCards = QJsonDocument::fromJson( jsonData, &error ).array();
  assert( error.error == QJsonParseError::NoError );

  mCardDB.clear();

  for( QJsonValueRef jsonCardRef : jsonCards ) {
    QJsonObject jsonCard = jsonCardRef.toObject();
    mCardDB[ jsonCard["id"].toString() ] = jsonCard.toVariantMap();
  }
}

void PaintHistoryInScreen( QPainter& painter, const OverlayHistoryWindow& wnd, const QPoint& pos ) {
  int padding = 10;

  QRect rect( pos.x() + 20, pos.y(), wnd.Width(), wnd.Height() );
  rect.translate( -qMax( rect.right() - painter.device()->width() + padding, 0 ), -qMax( rect.bottom() - painter.device()->height() + padding, 0 ) ); // fit to window
  wnd.Paint( painter, rect.x(), rect.y() );
}

void Overlay::paintEvent( QPaintEvent* ) {
  QString title;
  QRect rect;
  OverlayHistoryList *history = NULL;

  if( mShowPlayerHistory == PLAYER_SELF && mPlayerHistory.count() > 0 ) {
    title = "Cards drawn";
    history = &mPlayerHistory;
    rect = mPlayerDeckRect;
  } else if( mShowPlayerHistory == PLAYER_OPPONENT && mOpponentHistory.count() > 0 ) {
    title = "Cards played by opponent";
    history = &mOpponentHistory;
    rect = mOpponentDeckRect;
  }

  QPainter painter( this );
  painter.setRenderHint( QPainter::Antialiasing );

#ifdef Q_OS_WIN
  float rowFontSize = 9;
  float titleFontSize = 9;
#else
  float rowFontSize = 12;
  float titleFontSize = 12;
#endif

  int spacing = 8;
  int overlayWidth = 200;

  if( history ) {
    OverlayHistoryWindow wnd( title, *history, overlayWidth, spacing, spacing, titleFontSize, rowFontSize );
    QPoint pos = rect.center() + QPoint( rect.width() / 2 + 10, -wnd.Height() / 2 );
    PaintHistoryInScreen( painter, wnd, pos );
  }
}

void Overlay::HandleGameWindowChanged( int x, int y, int w, int h ) {
  // Order is important
  // Otherwise starting fullscreen on windows
  // will not show the overlay unless the FS mode is toggled
  setFixedSize( w, h );
  move( x, y );

  int minWidth = h * 4 / 3;
  mPlayerDeckRect = QRect( w / 2 + 0.440 * minWidth, h * 0.510, 0.05 * minWidth, h * 0.170 );
  mOpponentDeckRect = mPlayerDeckRect.translated( -0.005 * minWidth, -0.275 * h );

  Update();
}

void Overlay::Update() {
  if( Hearthstone::Instance()->GameRunning() && Settings::Instance()->OverlayEnabled() ) {
    show();
#ifdef Q_OS_WIN
    setAttribute( Qt::WA_QuitOnClose ); // otherwise taskkill /IM Track-o-Bot.exe does not work (http://www.qtcentre.org/threads/11713-Qt-Tool?p=62466#post62466)
#endif
  } else {
    hide();
  }

  update();
}

void Overlay::HandleGameStarted() {
  mCheckForHoverTimer.start( CHECK_FOR_OVERLAY_HOVER_INTERVAL_MS );
  Update();
}

void Overlay::HandleGameStopped() {
  mCheckForHoverTimer.stop();
  Update();
}

void Overlay::UpdateHistoryFor( Player player, const ::CardHistoryList& list ) {
  QMap< QString, QVariantMap > entries;

  for( const CardHistoryItem& it : list ) {
    const QString& cardId = it.cardId;

    if( cardId.isEmpty() ) {
      continue;
    }

    if( !mCardDB.contains( cardId ) ) {
      DBG( "Card %s not found", qt2cstr( cardId ) );
      continue;
    }

    if( mCardDB[ cardId ][ "type" ] == "hero" ) {
      continue;
    }

    if( it.player != player ) {
      continue;
    }

    QVariantMap& entry = entries[ cardId ];
    entry[ "count" ] = entry.value( "count", 0 ).toInt() + 1;
    entry[ "mana" ] = mCardDB[ cardId ][ "mana" ];
    entry[ "name" ] = mCardDB[ cardId ][ "name" ];
  }

  OverlayHistoryList* ref;
  if( player == PLAYER_SELF ) {
    ref = &mPlayerHistory;
  } else {
    ref = &mOpponentHistory;
  }

  *ref = entries.values();

  qSort( ref->begin(), ref->end(), []( const QVariantMap& a, const QVariantMap& b ) {
    if( a["mana"].toInt() == b["mana"].toInt() ) {
      return a["name"].toString() < b["name"].toString();
    } else {
      return a["mana"].toInt() < b["mana"].toInt();
    }
  });
}

void Overlay::HandleCardsDrawnUpdate( const ::CardHistoryList& cardsDrawn ) {
  UpdateHistoryFor( PLAYER_OPPONENT, cardsDrawn );
  UpdateHistoryFor( PLAYER_SELF, cardsDrawn );
  Update();
}

void Overlay::HandleOverlaySettingChanged( bool enabled ) {
  UNUSED_ARG( enabled );

  Update();
}
<|endoftext|>


<commit_before>#include <iostream>
#include <unistd.h>

#include "libs/catch/catch.hpp"

#include "src/dopt_node.h"

TEST_CASE("entangle|dopt_node-enc") {
	auto n = entangle::OTNode(8888, 100);
	entangle::upd_t u = { entangle::del, 100, 'c' };
	REQUIRE(n.enc_upd_t(u).compare("1:100:c") == 0);
	REQUIRE(n.cmp_upd_t({ entangle::del, 100, 'c'}, { entangle::del, 100, 'c' }) == true);
	REQUIRE(n.cmp_upd_t({ entangle::del, 100, 'c'}, { entangle::del, 100, 'd' }) == false);
	REQUIRE(n.cmp_upd_t(n.dec_upd_t("1:100:c"), { entangle::del, 100, 'c' }) == true);
}

TEST_CASE("entangle|dopt_node-insert") {
	auto s = entangle::OTNode(8000, 100);
	auto x = entangle::OTNode(8050, 1);
	// auto y = entangle::OTNode(8051, 1);
	// auto z = entangle::OTNode(8052, 1);

	REQUIRE_NOTHROW(s.up());
	REQUIRE_NOTHROW(x.up());
	REQUIRE(x.join("localhost", 8000) == true);

	REQUIRE(x.ins(0, '1') == true);
	sleep(1);

	REQUIRE(s.get_context().compare("1") == 0);
	REQUIRE(x.get_context().compare("1") == 0);

	REQUIRE(x.del(0) == true);
	sleep(1);

	REQUIRE(s.get_context().compare("") == 0);
	REQUIRE(x.get_context().compare("") == 0);

	REQUIRE(x.drop("localhost", 8000) == true);
	REQUIRE_NOTHROW(s.dn());
	REQUIRE_NOTHROW(x.dn());
}

TEST_CASE("entangle|dopt_node-daemon") {
	auto n = entangle::OTNode(8888, 100);
	auto m = entangle::OTNode(8889, 100);

	REQUIRE(m.join("localhost", 8888) == false);

	REQUIRE_NOTHROW(n.up());
	REQUIRE_NOTHROW(n.dn());

	REQUIRE_NOTHROW(n.up());
	REQUIRE_NOTHROW(m.up());
	REQUIRE(m.join("localhost", 8888) == true);
	sleep(1);
	REQUIRE(m.size() == 1);
	REQUIRE(n.size() == 1);
	REQUIRE(m.join("localhost", 8888) == false);
	REQUIRE(n.join("localhost", 8889) == false);
	REQUIRE(m.size() == 1);
	REQUIRE(n.size() == 1);
	REQUIRE(m.drop("localhost", 8888) == true);
	sleep(1);
	REQUIRE(m.size() == 0);
	REQUIRE(n.size() == 0);
	REQUIRE_NOTHROW(m.dn());
	REQUIRE_NOTHROW(n.dn());

	// auto-call OTNode::dn on stack unwind
	REQUIRE_NOTHROW(n.up());
	sleep(1);
}
<commit_msg>more tests<commit_after>#include <iostream>
#include <unistd.h>

#include "libs/catch/catch.hpp"

#include "src/dopt_node.h"

TEST_CASE("entangle|dopt_node-enc") {
	auto n = entangle::OTNode(8888, 100);
	entangle::upd_t u = { entangle::del, 100, 'c' };
	REQUIRE(n.enc_upd_t(u).compare("1:100:c") == 0);
	REQUIRE(n.cmp_upd_t({ entangle::del, 100, 'c'}, { entangle::del, 100, 'c' }) == true);
	REQUIRE(n.cmp_upd_t({ entangle::del, 100, 'c'}, { entangle::del, 100, 'd' }) == false);
	REQUIRE(n.cmp_upd_t(n.dec_upd_t("1:100:c"), { entangle::del, 100, 'c' }) == true);
}

TEST_CASE("entangle|dopt_node-insert") {
	auto s = entangle::OTNode(8000, 100);
	auto x = entangle::OTNode(8050, 1);
	auto y = entangle::OTNode(8051, 1);
	auto z = entangle::OTNode(8052, 1);

	REQUIRE_NOTHROW(s.up());
	REQUIRE_NOTHROW(x.up());
	REQUIRE_NOTHROW(y.up());
	REQUIRE_NOTHROW(z.up());
	REQUIRE(x.join("localhost", 8000) == true);
	REQUIRE(y.join("localhost", 8000) == true);
	REQUIRE(z.join("localhost", 8000) == true);

	REQUIRE(x.ins(0, '1') == true);
	sleep(1);

	REQUIRE(s.get_context().compare("1") == 0);
	REQUIRE(x.get_context().compare("1") == 0);
	REQUIRE(y.get_context().compare("1") == 0);
	REQUIRE(z.get_context().compare("1") == 0);

	REQUIRE(x.del(0) == true);
	sleep(1);

	REQUIRE(s.get_context().compare("") == 0);
	REQUIRE(x.get_context().compare("") == 0);
	REQUIRE(y.get_context().compare("") == 0);
	REQUIRE(z.get_context().compare("") == 0);

	REQUIRE(x.drop("localhost", 8000) == true);
	REQUIRE_NOTHROW(s.dn());
	REQUIRE_NOTHROW(x.dn());
	REQUIRE_NOTHROW(y.dn());
	REQUIRE_NOTHROW(z.dn());
}

TEST_CASE("entangle|dopt_node-daemon") {
	auto n = entangle::OTNode(8888, 100);
	auto m = entangle::OTNode(8889, 100);

	REQUIRE(m.join("localhost", 8888) == false);

	REQUIRE_NOTHROW(n.up());
	REQUIRE_NOTHROW(n.dn());

	REQUIRE_NOTHROW(n.up());
	REQUIRE_NOTHROW(m.up());
	REQUIRE(m.join("localhost", 8888) == true);
	sleep(1);
	REQUIRE(m.size() == 1);
	REQUIRE(n.size() == 1);
	REQUIRE(m.join("localhost", 8888) == false);
	REQUIRE(n.join("localhost", 8889) == false);
	REQUIRE(m.size() == 1);
	REQUIRE(n.size() == 1);
	REQUIRE(m.drop("localhost", 8888) == true);
	sleep(1);
	REQUIRE(m.size() == 0);
	REQUIRE(n.size() == 0);
	REQUIRE_NOTHROW(m.dn());
	REQUIRE_NOTHROW(n.dn());

	// auto-call OTNode::dn on stack unwind
	REQUIRE_NOTHROW(n.up());
	sleep(1);
}
<|endoftext|>


<commit_before>/************************************************************************************
 *   Copyright (C) 2014-2015 by Savoir-Faire Linux                                  *
 *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.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; either                   *
 *   version 2.1 of the License, or (at your option) any later version.             *
 *                                                                                *
 *   This library is distributed in the hope that it will be useful,                *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of                 *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU              *
 *   Lesser General Public License for more details.                                *
 *                                                                                *
 *   You should have received a copy of the GNU Lesser General Public               *
 *   License along with this library; if not, write to the Free Software            *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA *
 ***********************************************************************************/
#include "fallbackpersoncollection.h"

//Qt
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QHash>
#include <QtCore/QTimer>
#include <QtCore/QUrl>
#include <QtCore/QCryptographicHash>
#include <QtCore/QStandardPaths>

//Ring
#include "person.h"
#include "personmodel.h"
#include "private/vcardutils.h"
#include "contactmethod.h"
#include "collectioneditor.h"
#include "delegates/pixmapmanipulationdelegate.h"
#include "delegates/itemmodelstateserializationdelegate.h"
#include "private/threadworker.h"

class FallbackPersonBackendEditor final : public CollectionEditor<Person>
{
public:
   FallbackPersonBackendEditor(CollectionMediator<Person>* m, const QString& path) : CollectionEditor<Person>(m),m_Path(path) {}
   virtual bool save       ( const Person* item ) override;
   virtual bool remove     ( const Person* item ) override;
   virtual bool edit       ( Person*       item ) override;
   virtual bool addNew     ( const Person* item ) override;
   virtual bool addExisting( const Person* item ) override;

   QVector<Person*>             m_lItems;
   QString                      m_Path  ;
   QHash<const Person*,QString> m_hPaths;

private:
   virtual QVector<Person*> items() const override;
};

class FallbackPersonCollectionPrivate final : public QObject
{
   Q_OBJECT
public:
   FallbackPersonCollectionPrivate(FallbackPersonCollection* parent, CollectionMediator<Person>* mediator, const QString& path);
   CollectionMediator<Person>*  m_pMediator;
   QString                      m_Path     ;
   QString                      m_Name     ;

   FallbackPersonCollection* q_ptr;

public Q_SLOTS:
   void loadAsync();
};

FallbackPersonCollectionPrivate::FallbackPersonCollectionPrivate(FallbackPersonCollection* parent, CollectionMediator<Person>* mediator, const QString& path) : q_ptr(parent), m_pMediator(mediator), m_Path(path)
{
   //Default to somewhere ~/.local/share
   if (m_Path.isEmpty()) {
      m_Path = (QStandardPaths::writableLocation(QStandardPaths::DataLocation)) + "/vCard/";
      static_cast<FallbackPersonBackendEditor*>(q_ptr->editor<Person>())->m_Path = m_Path;
   }

   m_Name = path.split('/').last();
   if (m_Name.size())
      m_Name[0] = m_Name[0].toUpper();
   else
      m_Name = "vCard";
}

FallbackPersonCollection::FallbackPersonCollection(CollectionMediator<Person>* mediator, const QString& path, FallbackPersonCollection* parent) :
CollectionInterface(new FallbackPersonBackendEditor(mediator,path),parent),d_ptr(new FallbackPersonCollectionPrivate(this,mediator,path))
{
}

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

bool FallbackPersonBackendEditor::save(const Person* item)
{
   if (!item)
      return false;

   //An UID is required
   if (item->uid().isEmpty()) {
      QCryptographicHash hash(QCryptographicHash::Sha1);
      for (ContactMethod* n : item->phoneNumbers())
         hash.addData(n->uri().toLatin1());
      hash.addData(item->formattedName().toLatin1());
      QByteArray random;

      for (int i=0;i<5;i++)
         random.append(QChar((char)(rand()%255)));

      hash.addData(random);

      const_cast<Person*>(item)->setUid(hash.result().toHex());
   }

   QFile file(m_Path+'/'+item->uid()+".vcf");
   file.open(QIODevice::WriteOnly);
   file.write(item->toVCard({}));
   file.close();
   return true;
}

bool FallbackPersonBackendEditor::remove(const Person* item)
{
   if (!item)
      return false;

   QString path = m_hPaths[item];

   if (path.isEmpty())
      path = m_Path+'/'+item->uid()+".vcf";

   bool ret = QFile::remove(path);

   if (ret) {
      ret &= mediator()->removeItem(item);
   }

   return ret;
}

bool FallbackPersonBackendEditor::edit( Person* item)
{
   Q_UNUSED(item)
   return false;
}

bool FallbackPersonBackendEditor::addNew(const Person* item)
{
   bool ret = save(item);

   if (ret) {
      addExisting(item);
   }

   return ret;
}

bool FallbackPersonBackendEditor::addExisting(const Person* item)
{
   m_lItems << const_cast<Person*>(item);
   mediator()->addItem(item);
   return true;
}

QVector<Person*> FallbackPersonBackendEditor::items() const
{
   return m_lItems;
}

QString FallbackPersonCollection::name () const
{
   return d_ptr->m_Name;
}

QString FallbackPersonCollection::category () const
{
   return QObject::tr("Contact");
}

QVariant FallbackPersonCollection::icon() const
{
   return PixmapManipulationDelegate::instance()->collectionIcon(this,PixmapManipulationDelegate::CollectionIconHint::CONTACT);
}

bool FallbackPersonCollection::isEnabled() const
{
   /* if delegate exists, check if collectin is enabled, else assume it is */
   if (auto delegate = ItemModelStateSerializationDelegate::instance())
      return delegate->isChecked(this);
   else
      return true;
}

bool FallbackPersonCollection::load()
{
   new ThreadWorker([this]() {
      bool ok;
      Q_UNUSED(ok)
      QList< Person* > ret =  VCardUtils::loadDir(QUrl(d_ptr->m_Path),ok,static_cast<FallbackPersonBackendEditor*>(editor<Person>())->m_hPaths);
      for(Person* p : ret) {
         p->setCollection(this);
         editor<Person>()->addExisting(p);
      }
   });

   //Add all sub directories as new backends
   QTimer::singleShot(0,d_ptr,SLOT(loadAsync()));

   return true;
}

bool FallbackPersonCollection::reload()
{
   return false;
}

FlagPack<CollectionInterface::SupportedFeatures> FallbackPersonCollection::supportedFeatures() const
{
   return
      CollectionInterface::SupportedFeatures::NONE       |
      CollectionInterface::SupportedFeatures::LOAD       |
      CollectionInterface::SupportedFeatures::CLEAR      |
      CollectionInterface::SupportedFeatures::MANAGEABLE |
      CollectionInterface::SupportedFeatures::REMOVE     |
      CollectionInterface::SupportedFeatures::ADD        ;
}

bool FallbackPersonCollection::clear()
{
   QDir dir(d_ptr->m_Path);
   for (const QString& file : dir.entryList({"*.vcf"},QDir::Files))
      dir.remove(file);
   return true;
}

QByteArray FallbackPersonCollection::id() const
{
   return "fpc2"+d_ptr->m_Path.toLatin1();
}

void FallbackPersonCollectionPrivate::loadAsync()
{
   QDir d(m_Path);
   for (const QString& dir : d.entryList(QDir::AllDirs)) {
      if (dir != QString('.') && dir != "..") {
         CollectionInterface* col = PersonModel::instance()->addCollection<FallbackPersonCollection,QString,FallbackPersonCollection*>(m_Path+'/'+dir,q_ptr);
         if (col->isEnabled()) {
            col->load();
         }
      }
   }
}

#include "fallbackpersoncollection.moc"
<commit_msg>vcard: make sure vcard dir exists<commit_after>/************************************************************************************
 *   Copyright (C) 2014-2015 by Savoir-Faire Linux                                  *
 *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.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; either                   *
 *   version 2.1 of the License, or (at your option) any later version.             *
 *                                                                                *
 *   This library is distributed in the hope that it will be useful,                *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of                 *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU              *
 *   Lesser General Public License for more details.                                *
 *                                                                                *
 *   You should have received a copy of the GNU Lesser General Public               *
 *   License along with this library; if not, write to the Free Software            *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA *
 ***********************************************************************************/
#include "fallbackpersoncollection.h"

//Qt
#include <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QHash>
#include <QtCore/QTimer>
#include <QtCore/QUrl>
#include <QtCore/QCryptographicHash>
#include <QtCore/QStandardPaths>

//Ring
#include "person.h"
#include "personmodel.h"
#include "private/vcardutils.h"
#include "contactmethod.h"
#include "collectioneditor.h"
#include "delegates/pixmapmanipulationdelegate.h"
#include "delegates/itemmodelstateserializationdelegate.h"
#include "private/threadworker.h"

class FallbackPersonBackendEditor final : public CollectionEditor<Person>
{
public:
   FallbackPersonBackendEditor(CollectionMediator<Person>* m, const QString& path) : CollectionEditor<Person>(m),m_Path(path) {}
   virtual bool save       ( const Person* item ) override;
   virtual bool remove     ( const Person* item ) override;
   virtual bool edit       ( Person*       item ) override;
   virtual bool addNew     ( const Person* item ) override;
   virtual bool addExisting( const Person* item ) override;

   QVector<Person*>             m_lItems;
   QString                      m_Path  ;
   QHash<const Person*,QString> m_hPaths;

private:
   virtual QVector<Person*> items() const override;
};

class FallbackPersonCollectionPrivate final : public QObject
{
   Q_OBJECT
public:
   FallbackPersonCollectionPrivate(FallbackPersonCollection* parent, CollectionMediator<Person>* mediator, const QString& path);
   CollectionMediator<Person>*  m_pMediator;
   QString                      m_Path     ;
   QString                      m_Name     ;

   FallbackPersonCollection* q_ptr;

public Q_SLOTS:
   void loadAsync();
};

FallbackPersonCollectionPrivate::FallbackPersonCollectionPrivate(FallbackPersonCollection* parent, CollectionMediator<Person>* mediator, const QString& path) : q_ptr(parent), m_pMediator(mediator), m_Path(path)
{
   //Default to somewhere ~/.local/share
   if (m_Path.isEmpty()) {
      m_Path = (QStandardPaths::writableLocation(QStandardPaths::DataLocation)) + "/vCard/";
      static_cast<FallbackPersonBackendEditor*>(q_ptr->editor<Person>())->m_Path = m_Path;
   }
   //Make sure the directory exists so that saving new contacts there doesn't fail
   if (!QDir().mkpath(m_Path))
      qWarning() << "cannot create path for fallbackcollection: " << m_Path;

   m_Name = path.split('/').last();
   if (m_Name.size())
      m_Name[0] = m_Name[0].toUpper();
   else
      m_Name = "vCard";
}

FallbackPersonCollection::FallbackPersonCollection(CollectionMediator<Person>* mediator, const QString& path, FallbackPersonCollection* parent) :
CollectionInterface(new FallbackPersonBackendEditor(mediator,path),parent),d_ptr(new FallbackPersonCollectionPrivate(this,mediator,path))
{
}

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

bool FallbackPersonBackendEditor::save(const Person* item)
{
   if (!item)
      return false;

   //An UID is required
   if (item->uid().isEmpty()) {
      QCryptographicHash hash(QCryptographicHash::Sha1);
      for (ContactMethod* n : item->phoneNumbers())
         hash.addData(n->uri().toLatin1());
      hash.addData(item->formattedName().toLatin1());
      QByteArray random;

      for (int i=0;i<5;i++)
         random.append(QChar((char)(rand()%255)));

      hash.addData(random);

      const_cast<Person*>(item)->setUid(hash.result().toHex());
   }

   QFile file(m_Path+'/'+item->uid()+".vcf");
   file.open(QIODevice::WriteOnly);
   file.write(item->toVCard({}));
   file.close();
   return true;
}

bool FallbackPersonBackendEditor::remove(const Person* item)
{
   if (!item)
      return false;

   QString path = m_hPaths[item];

   if (path.isEmpty())
      path = m_Path+'/'+item->uid()+".vcf";

   bool ret = QFile::remove(path);

   if (ret) {
      ret &= mediator()->removeItem(item);
   }

   return ret;
}

bool FallbackPersonBackendEditor::edit( Person* item)
{
   Q_UNUSED(item)
   return false;
}

bool FallbackPersonBackendEditor::addNew(const Person* item)
{
   bool ret = save(item);

   if (ret) {
      addExisting(item);
   }

   return ret;
}

bool FallbackPersonBackendEditor::addExisting(const Person* item)
{
   m_lItems << const_cast<Person*>(item);
   mediator()->addItem(item);
   return true;
}

QVector<Person*> FallbackPersonBackendEditor::items() const
{
   return m_lItems;
}

QString FallbackPersonCollection::name () const
{
   return d_ptr->m_Name;
}

QString FallbackPersonCollection::category () const
{
   return QObject::tr("Contact");
}

QVariant FallbackPersonCollection::icon() const
{
   return PixmapManipulationDelegate::instance()->collectionIcon(this,PixmapManipulationDelegate::CollectionIconHint::CONTACT);
}

bool FallbackPersonCollection::isEnabled() const
{
   /* if delegate exists, check if collectin is enabled, else assume it is */
   if (auto delegate = ItemModelStateSerializationDelegate::instance())
      return delegate->isChecked(this);
   else
      return true;
}

bool FallbackPersonCollection::load()
{
   new ThreadWorker([this]() {
      bool ok;
      Q_UNUSED(ok)
      QList< Person* > ret =  VCardUtils::loadDir(QUrl(d_ptr->m_Path),ok,static_cast<FallbackPersonBackendEditor*>(editor<Person>())->m_hPaths);
      for(Person* p : ret) {
         p->setCollection(this);
         editor<Person>()->addExisting(p);
      }
   });

   //Add all sub directories as new backends
   QTimer::singleShot(0,d_ptr,SLOT(loadAsync()));

   return true;
}

bool FallbackPersonCollection::reload()
{
   return false;
}

FlagPack<CollectionInterface::SupportedFeatures> FallbackPersonCollection::supportedFeatures() const
{
   return
      CollectionInterface::SupportedFeatures::NONE       |
      CollectionInterface::SupportedFeatures::LOAD       |
      CollectionInterface::SupportedFeatures::CLEAR      |
      CollectionInterface::SupportedFeatures::MANAGEABLE |
      CollectionInterface::SupportedFeatures::REMOVE     |
      CollectionInterface::SupportedFeatures::ADD        ;
}

bool FallbackPersonCollection::clear()
{
   QDir dir(d_ptr->m_Path);
   for (const QString& file : dir.entryList({"*.vcf"},QDir::Files))
      dir.remove(file);
   return true;
}

QByteArray FallbackPersonCollection::id() const
{
   return "fpc2"+d_ptr->m_Path.toLatin1();
}

void FallbackPersonCollectionPrivate::loadAsync()
{
   QDir d(m_Path);
   for (const QString& dir : d.entryList(QDir::AllDirs)) {
      if (dir != QString('.') && dir != "..") {
         CollectionInterface* col = PersonModel::instance()->addCollection<FallbackPersonCollection,QString,FallbackPersonCollection*>(m_Path+'/'+dir,q_ptr);
         if (col->isEnabled()) {
            col->load();
         }
      }
   }
}

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


<commit_before>#include "saiga/util/crash.h"
#include "saiga/util/assert.h"

#include <iostream>

std::function<void()> customCrashHandler;

void addCustomSegfaultHandler(std::function<void()> fnc)
{
	customCrashHandler = fnc;
}

#if defined(__unix__)

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>

//Source: http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes

/* This structure mirrors the one found in /usr/include/asm/ucontext.h */
typedef struct _sig_ucontext {
    unsigned long     uc_flags;
    struct ucontext   *uc_link;
    stack_t           uc_stack;
    struct sigcontext uc_mcontext;
    sigset_t          uc_sigmask;
} sig_ucontext_t;

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
    void *             array[50];
    void *             caller_address;
    char **            messages;
    int                size, i;
    sig_ucontext_t *   uc;

    uc = (sig_ucontext_t *)ucontext;

    /* Get the address at the time the signal was raised */
#if defined(__i386__) // gcc specific
    caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific
#elif defined(__x86_64__) // gcc specific
    caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific
#else
#error Unsupported architecture. // TODO: Add support for other arch.
#endif

    std:: cout << "signal " << sig_num << " (" << strsignal(sig_num) << ")"
               << ", address is " << info->si_addr << " from " << (void *)caller_address << std::endl;

    size = backtrace(array, 50);

    /* overwrite sigaction with caller's address */
    array[1] = caller_address;

    messages = backtrace_symbols(array, size);

    /* skip first stack frame (points here) */
    for (i = 1; i < size && messages != NULL; ++i){
        std::cout << "[bt]: (" << i << ") " << messages[i] << std::endl;
    }

    free(messages);

    if(customCrashHandler)
        customCrashHandler();

    //make sure the program exits here, because otherwise the programm will continue after the segfault
    SAIGA_ASSERT(0);
    exit(EXIT_FAILURE);
}

void catchSegFaults()
{
    struct sigaction sigact;

    sigact.sa_sigaction = crit_err_hdlr;
    sigact.sa_flags = SA_RESTART | SA_SIGINFO;

    if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0){
        std::cerr << "error setting signal handlern" << std::endl;
        SAIGA_ASSERT(0);
    }
}

#endif

#if defined(_WIN32)

#include <windows.h>
#include <DbgHelp.h>

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <tchar.h>

//The code requires you to link against the DbgHelp.lib library

void printCurrentStack() {
	std::string outWalk;
	// Set up the symbol options so that we can gather information from the current
	// executable's PDB files, as well as the Microsoft symbol servers.  We also want
	// to undecorate the symbol names we're returned.  If you want, you can add other
	// symbol servers or paths via a semi-colon separated list in SymInitialized.
	::SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_UNDNAME);
	if (!::SymInitialize(::GetCurrentProcess(), "http://msdl.microsoft.com/download/symbols", TRUE)) return;

	// Capture up to 25 stack frames from the current call stack.  We're going to
	// skip the first stack frame returned because that's the GetStackWalk function
	// itself, which we don't care about.
	PVOID addrs[25] = { 0 };
	USHORT frames = CaptureStackBackTrace(1, 25, addrs, NULL);

	for (USHORT i = 0; i < frames; i++) {
		// Allocate a buffer large enough to hold the symbol information on the stack and get
		// a pointer to the buffer.  We also have to set the size of the symbol structure itself
		// and the number of bytes reserved for the name.
		ULONG64 buffer[(sizeof(SYMBOL_INFO) + 1024 + sizeof(ULONG64) - 1) / sizeof(ULONG64)] = { 0 };
		SYMBOL_INFO *info = (SYMBOL_INFO *)buffer;
		info->SizeOfStruct = sizeof(SYMBOL_INFO);
		info->MaxNameLen = 1024;

		// Attempt to get information about the symbol and add it to our output parameter.
		DWORD64 displacement = 0;
		if (::SymFromAddr(::GetCurrentProcess(), (DWORD64)addrs[i], &displacement, info)) {
			//outWalk.append(info->Name, info->NameLen);
			//outWalk.append("\n");
			std::cout << "[bt]: (" << i << ") " << info->Name << std::endl;
		}
	}

	::SymCleanup(::GetCurrentProcess());

}

void SignalHandler(int signal)
{
	printCurrentStack();

	if (customCrashHandler)
		customCrashHandler();

	//make sure the program exits here, because otherwise the programm will continue after the segfault
	SAIGA_ASSERT(0);
	exit(EXIT_FAILURE);
}

void catchSegFaults()
{
	typedef void(*SignalHandlerPointer)(int);

	SignalHandlerPointer previousHandler;
	previousHandler = signal(SIGSEGV, SignalHandler);
}
#endif<commit_msg>crash<commit_after>#include "saiga/util/crash.h"
#include "saiga/util/assert.h"

#include <iostream>

std::function<void()> customCrashHandler;

void addCustomSegfaultHandler(std::function<void()> fnc)
{
	customCrashHandler = fnc;
}

#if defined(__unix__)

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>

//Source: http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes

/* This structure mirrors the one found in /usr/include/asm/ucontext.h */
typedef struct _sig_ucontext {
    unsigned long     uc_flags;
    struct ucontext   *uc_link;
    stack_t           uc_stack;
    struct sigcontext uc_mcontext;
    sigset_t          uc_sigmask;
} sig_ucontext_t;

void printCurrentStack(){
    void *             array[50];
    char **            messages;
    int                size, i;

    size = backtrace(array, 50);

    /* overwrite sigaction with caller's address */
//    array[1] = caller_address;

    messages = backtrace_symbols(array, size);

    /* skip first stack frame (points here) */
    for (i = 0; i < size && messages != NULL; ++i){
        std::cout << "[bt]: (" << i << ") " << messages[i] << std::endl;
    }

    free(messages);
}

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
    void *             caller_address;
    sig_ucontext_t *   uc;

    uc = (sig_ucontext_t *)ucontext;

    /* Get the address at the time the signal was raised */
#if defined(__i386__) // gcc specific
    caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific
#elif defined(__x86_64__) // gcc specific
    caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific
#else
#error Unsupported architecture. // TODO: Add support for other arch.
#endif

    std:: cout << "signal " << sig_num << " (" << strsignal(sig_num) << ")"
               << ", address is " << info->si_addr << " from " << (void *)caller_address << std::endl;

    printCurrentStack();

    if(customCrashHandler)
        customCrashHandler();

    //make sure the program exits here, because otherwise the programm will continue after the segfault
    SAIGA_ASSERT(0);
    exit(EXIT_FAILURE);
}

void catchSegFaults()
{
    struct sigaction sigact;

    sigact.sa_sigaction = crit_err_hdlr;
    sigact.sa_flags = SA_RESTART | SA_SIGINFO;

    if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0){
        std::cerr << "error setting signal handlern" << std::endl;
        SAIGA_ASSERT(0);
    }
}

#endif

#if defined(_WIN32)

#include <windows.h>
#include <DbgHelp.h>

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <tchar.h>

//The code requires you to link against the DbgHelp.lib library

void printCurrentStack() {
	std::string outWalk;
	// Set up the symbol options so that we can gather information from the current
	// executable's PDB files, as well as the Microsoft symbol servers.  We also want
	// to undecorate the symbol names we're returned.  If you want, you can add other
	// symbol servers or paths via a semi-colon separated list in SymInitialized.
	::SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_UNDNAME);
	if (!::SymInitialize(::GetCurrentProcess(), "http://msdl.microsoft.com/download/symbols", TRUE)) return;

	// Capture up to 25 stack frames from the current call stack.  We're going to
	// skip the first stack frame returned because that's the GetStackWalk function
	// itself, which we don't care about.
    const int numAddrs = 50;
    PVOID addrs[numAddrs] = { 0 };
    USHORT frames = CaptureStackBackTrace(0, numAddrs-1, addrs, NULL);

	for (USHORT i = 0; i < frames; i++) {
		// Allocate a buffer large enough to hold the symbol information on the stack and get
		// a pointer to the buffer.  We also have to set the size of the symbol structure itself
		// and the number of bytes reserved for the name.
		ULONG64 buffer[(sizeof(SYMBOL_INFO) + 1024 + sizeof(ULONG64) - 1) / sizeof(ULONG64)] = { 0 };
		SYMBOL_INFO *info = (SYMBOL_INFO *)buffer;
		info->SizeOfStruct = sizeof(SYMBOL_INFO);
		info->MaxNameLen = 1024;

		// Attempt to get information about the symbol and add it to our output parameter.
		DWORD64 displacement = 0;
		if (::SymFromAddr(::GetCurrentProcess(), (DWORD64)addrs[i], &displacement, info)) {
			//outWalk.append(info->Name, info->NameLen);
			//outWalk.append("\n");
			std::cout << "[bt]: (" << i << ") " << info->Name << std::endl;
		}
	}

	::SymCleanup(::GetCurrentProcess());

}

void SignalHandler(int signal)
{
	printCurrentStack();

	if (customCrashHandler)
		customCrashHandler();

	//make sure the program exits here, because otherwise the programm will continue after the segfault
	SAIGA_ASSERT(0);
	exit(EXIT_FAILURE);
}

void catchSegFaults()
{
	typedef void(*SignalHandlerPointer)(int);

	SignalHandlerPointer previousHandler;
	previousHandler = signal(SIGSEGV, SignalHandler);
}
#endif
<|endoftext|>


<commit_before>#include "tests/lib/test.h"

#include <cstdlib>

#include <QtCore/QTimer>

#include <TelepathyQt4/Types>
#include <TelepathyQt4/Debug>
#include <TelepathyQt4/DBus>
#include <TelepathyQt4/PendingVoid>
#include <TelepathyQt4/SharedPtr>
#include <TelepathyQt4/RefCounted>

using Tp::PendingOperation;
using Tp::PendingVoid;
using Tp::Client::DBus::PeerInterface;

Test::Test(QObject *parent)
    : QObject(parent), mLoop(new QEventLoop(this))
{
    QTimer::singleShot(10 * 60 * 1000, this, SLOT(onWatchdog()));
}

Test::~Test()
{
    delete mLoop;
}

void Test::initTestCaseImpl()
{
    Tp::registerTypes();
    Tp::enableDebug(true);
    Tp::enableWarnings(true);

    QVERIFY(QDBusConnection::sessionBus().isConnected());
}

void Test::initImpl()
{
}

void Test::cleanupImpl()
{
}

void Test::cleanupTestCaseImpl()
{
    // To allow for cleanup code to run (e.g. PendingOperation cleanup after they finish)
    mLoop->processEvents();
}

void Test::expectSuccessfulCall(PendingOperation *op)
{
    if (op->isError()) {
        qWarning().nospace() << op->errorName()
            << ": " << op->errorMessage();
        mLoop->exit(1);
        return;
    }

    mLoop->exit(0);
}

void Test::expectSuccessfulCall(QDBusPendingCallWatcher *watcher)
{
    if (watcher->isError()) {
        qWarning().nospace() << watcher->error().name()
            << ": " << watcher->error().message();
        mLoop->exit(1);
        return;
    }

    mLoop->exit(0);
}

void Test::expectFailure(PendingOperation *op)
{
    if (!op->isError()) {
        qWarning() << "expectFailure(): should have been an error, but wasn't";
        mLoop->exit(1);
        return;
    }

    mLoop->exit(0);
}

void Test::expectSuccessfulProperty(PendingOperation *op)
{
    if (op->isError()) {
        qWarning().nospace() << op->errorName()
            << ": " << op->errorMessage();
        mPropertyValue = QVariant();
        mLoop->exit(1);
    } else {
        Tp::PendingVariant *pv = qobject_cast<Tp::PendingVariant*>(op);
        mPropertyValue = pv->result();
        mLoop->exit(0);
    }
}

void Test::processDBusQueue(Tp::DBusProxy *proxy)
{
    // Call method Ping on the D-Bus Peer interface
    PeerInterface peer(proxy);
    PendingVoid *call = new PendingVoid(peer.Ping(), Tp::SharedPtr<Tp::RefCounted>());

    // Wait for the reply to the Ping call
    while (!call->isFinished()) {
        mLoop->processEvents();
    }

    QVERIFY(call->isFinished());
    QVERIFY(call->isValid());
}

void Test::onWatchdog()
{
    // We can't use QFAIL because the test would then go to cleanup() and/or cleanupTestCase(),
    // which would often hang too - so let's just use abort
    qWarning() << "Test took over 10 minutes to finish, it's probably hung up - aborting";
    std::abort();
}

#include "_gen/test.h.moc.hpp"
<commit_msg>Make processDBusQueue do an extra mainloop iteration so the PendingVoid is not reported as a leak<commit_after>#include "tests/lib/test.h"

#include <cstdlib>

#include <QtCore/QTimer>

#include <TelepathyQt4/Types>
#include <TelepathyQt4/Debug>
#include <TelepathyQt4/DBus>
#include <TelepathyQt4/PendingVoid>
#include <TelepathyQt4/SharedPtr>
#include <TelepathyQt4/RefCounted>

using Tp::PendingOperation;
using Tp::PendingVoid;
using Tp::Client::DBus::PeerInterface;

Test::Test(QObject *parent)
    : QObject(parent), mLoop(new QEventLoop(this))
{
    QTimer::singleShot(10 * 60 * 1000, this, SLOT(onWatchdog()));
}

Test::~Test()
{
    delete mLoop;
}

void Test::initTestCaseImpl()
{
    Tp::registerTypes();
    Tp::enableDebug(true);
    Tp::enableWarnings(true);

    QVERIFY(QDBusConnection::sessionBus().isConnected());
}

void Test::initImpl()
{
}

void Test::cleanupImpl()
{
}

void Test::cleanupTestCaseImpl()
{
    // To allow for cleanup code to run (e.g. PendingOperation cleanup after they finish)
    mLoop->processEvents();
}

void Test::expectSuccessfulCall(PendingOperation *op)
{
    if (op->isError()) {
        qWarning().nospace() << op->errorName()
            << ": " << op->errorMessage();
        mLoop->exit(1);
        return;
    }

    mLoop->exit(0);
}

void Test::expectSuccessfulCall(QDBusPendingCallWatcher *watcher)
{
    if (watcher->isError()) {
        qWarning().nospace() << watcher->error().name()
            << ": " << watcher->error().message();
        mLoop->exit(1);
        return;
    }

    mLoop->exit(0);
}

void Test::expectFailure(PendingOperation *op)
{
    if (!op->isError()) {
        qWarning() << "expectFailure(): should have been an error, but wasn't";
        mLoop->exit(1);
        return;
    }

    mLoop->exit(0);
}

void Test::expectSuccessfulProperty(PendingOperation *op)
{
    if (op->isError()) {
        qWarning().nospace() << op->errorName()
            << ": " << op->errorMessage();
        mPropertyValue = QVariant();
        mLoop->exit(1);
    } else {
        Tp::PendingVariant *pv = qobject_cast<Tp::PendingVariant*>(op);
        mPropertyValue = pv->result();
        mLoop->exit(0);
    }
}

void Test::processDBusQueue(Tp::DBusProxy *proxy)
{
    // Call method Ping on the D-Bus Peer interface
    PeerInterface peer(proxy);
    PendingVoid *call = new PendingVoid(peer.Ping(), Tp::SharedPtr<Tp::RefCounted>());

    // Wait for the reply to the Ping call
    while (!call->isFinished()) {
        mLoop->processEvents();
    }

    QVERIFY(call->isFinished());
    QVERIFY(call->isValid());

    // Do one more processEvents so the PendingVoid is always freed
    mLoop->processEvents();
}

void Test::onWatchdog()
{
    // We can't use QFAIL because the test would then go to cleanup() and/or cleanupTestCase(),
    // which would often hang too - so let's just use abort
    qWarning() << "Test took over 10 minutes to finish, it's probably hung up - aborting";
    std::abort();
}

#include "_gen/test.h.moc.hpp"
<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/**
 * $Log$
 * Revision 1.2  2000/01/15 01:26:16  rahulj
 * Added support for HTTP to the parser using libWWW 5.2.8.
 * Renamed URL.[ch]pp to XMLURL.[ch]pp and like wise for the class name.
 * Only tested under NT 4.0 SP 5.
 * Removed URL.hpp from files where it was not used.
 *
 * Revision 1.1  2000/01/12 00:13:26  roddey
 * These were moved from internal/ to framework/, which was something that should have
 * happened long ago. They are really framework type of classes.
 *
 * Revision 1.1.1.1  1999/11/09 01:08:18  twl
 * Initial checkin
 *
 * Revision 1.2  1999/11/08 20:44:44  rahul
 * Swat for adding in Product name and CVS comment log variable.
 *
 */

#if !defined(URLINPUTSOURCE_HPP)
#define URLINPUTSOURCE_HPP

#include <sax/InputSource.hpp>

class XMLPARSER_EXPORT URLInputSource : public InputSource
{
public :
    // -----------------------------------------------------------------------
    //  Constructors and Destructor
    // -----------------------------------------------------------------------
    URLInputSource(const XMLURL& urlId);
    URLInputSource
    (
        const   XMLCh* const    baseId
        , const XMLCh* const    systemId
    );
    URLInputSource
    (
        const   XMLCh* const    baseId
        , const XMLCh* const    systemId
        , const XMLCh* const    publicId
    );
    URLInputSource
    (
        const   XMLCh* const    baseId
        , const char* const     systemId
    );
    URLInputSource
    (
        const   XMLCh* const    baseId
        , const char* const     systemId
        , const char* const     publicId
    );
    ~URLInputSource();

    // -----------------------------------------------------------------------
    //  Virtual input source interface
    // -----------------------------------------------------------------------
    BinInputStream* makeStream() const;

    // -----------------------------------------------------------------------
    //  Getter methods
    // -----------------------------------------------------------------------
    const XMLURL& urlSrc() const;

private :
    // -----------------------------------------------------------------------
    //  Private data members
    //
    //  fURL
    //      This is the URL created from the passed ids.
    // -----------------------------------------------------------------------
    XMLURL fURL;
};

inline const XMLURL& URLInputSource::urlSrc() const
{
    return fURL;
}

#endif
<commit_msg>Needed to include XMLURL.hpp so that it compiles standalone.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/**
 * $Log$
 * Revision 1.3  2000/01/26 18:56:02  roddey
 * Needed to include XMLURL.hpp so that it compiles standalone.
 *
 * Revision 1.2  2000/01/15 01:26:16  rahulj
 * Added support for HTTP to the parser using libWWW 5.2.8.
 * Renamed URL.[ch]pp to XMLURL.[ch]pp and like wise for the class name.
 * Only tested under NT 4.0 SP 5.
 * Removed URL.hpp from files where it was not used.
 *
 * Revision 1.1  2000/01/12 00:13:26  roddey
 * These were moved from internal/ to framework/, which was something that should have
 * happened long ago. They are really framework type of classes.
 *
 * Revision 1.1.1.1  1999/11/09 01:08:18  twl
 * Initial checkin
 *
 * Revision 1.2  1999/11/08 20:44:44  rahul
 * Swat for adding in Product name and CVS comment log variable.
 *
 */

#if !defined(URLINPUTSOURCE_HPP)
#define URLINPUTSOURCE_HPP

#include <util/XMLURL.hpp>
#include <sax/InputSource.hpp>

class XMLPARSER_EXPORT URLInputSource : public InputSource
{
public :
    // -----------------------------------------------------------------------
    //  Constructors and Destructor
    // -----------------------------------------------------------------------
    URLInputSource(const XMLURL& urlId);
    URLInputSource
    (
        const   XMLCh* const    baseId
        , const XMLCh* const    systemId
    );
    URLInputSource
    (
        const   XMLCh* const    baseId
        , const XMLCh* const    systemId
        , const XMLCh* const    publicId
    );
    URLInputSource
    (
        const   XMLCh* const    baseId
        , const char* const     systemId
    );
    URLInputSource
    (
        const   XMLCh* const    baseId
        , const char* const     systemId
        , const char* const     publicId
    );
    ~URLInputSource();

    // -----------------------------------------------------------------------
    //  Virtual input source interface
    // -----------------------------------------------------------------------
    BinInputStream* makeStream() const;

    // -----------------------------------------------------------------------
    //  Getter methods
    // -----------------------------------------------------------------------
    const XMLURL& urlSrc() const;

private :
    // -----------------------------------------------------------------------
    //  Private data members
    //
    //  fURL
    //      This is the URL created from the passed ids.
    // -----------------------------------------------------------------------
    XMLURL fURL;
};

inline const XMLURL& URLInputSource::urlSrc() const
{
    return fURL;
}

#endif
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    TestCubeAxes3.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

// This example illustrates how one may explicitly specify the range of each
// axes that's used to define the prop, while displaying data with a different
// set of bounds (unlike cubeAxes2.tcl). This example allows you to separate
// the notion of extent of the axes in physical space (bounds) and the extent
// of the values it represents. In other words, you can have the ticks and
// labels show a different range.

#include "vtkBYUReader.h"
#include "vtkCamera.h"
#include "vtkCubeAxesActor.h"
#include "vtkLight.h"
#include "vtkLODActor.h"
#include "vtkNew.h"
#include "vtkOutlineFilter.h"
#include "vtkPolyDataMapper.h"
#include "vtkPolyDataNormals.h"
#include "vtkProperty.h"
#include "vtkRegressionTestImage.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"
#include "vtkTestUtilities.h"

//----------------------------------------------------------------------------
int TestCubeAxesWithZInnerPolys( int argc, char * argv [] )
{
  vtkNew<vtkBYUReader> fohe;
  char* fname = vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/teapot.g");
  fohe->SetGeometryFileName(fname);
  delete [] fname;

  vtkNew<vtkPolyDataNormals> normals;
  normals->SetInputConnection(fohe->GetOutputPort());

  vtkNew<vtkPolyDataMapper> foheMapper;
  foheMapper->SetInputConnection(normals->GetOutputPort());

  vtkNew<vtkLODActor> foheActor;
  foheActor->SetMapper(foheMapper.GetPointer());
  foheActor->GetProperty()->SetDiffuseColor(0.7, 0.3, 0.0);

  vtkNew<vtkOutlineFilter> outline;
  outline->SetInputConnection(normals->GetOutputPort());

  vtkNew<vtkPolyDataMapper> mapOutline;
  mapOutline->SetInputConnection(outline->GetOutputPort());

  vtkNew<vtkActor> outlineActor;
  outlineActor->SetMapper(mapOutline.GetPointer());
  outlineActor->GetProperty()->SetColor(0.0 ,0.0 ,0.0);

  vtkNew<vtkCamera> camera;
  camera->SetClippingRange(1.0, 100.0);
  camera->SetFocalPoint(0.9, 1.0, 0.0);
  camera->SetPosition(11.63, 6.0, 10.77);

  vtkNew<vtkLight> light;
  light->SetFocalPoint(0.21406, 1.5, 0.0);
  light->SetPosition(8.3761, 4.94858, 4.12505);

  vtkNew<vtkRenderer> ren2;
  ren2->SetActiveCamera(camera.GetPointer());
  ren2->AddLight(light.GetPointer());

  vtkNew<vtkRenderWindow> renWin;
  renWin->SetMultiSamples(0);
  renWin->AddRenderer(ren2.GetPointer());
  renWin->SetWindowName("VTK - Cube Axes custom range");
  renWin->SetSize(600, 600);

  vtkNew<vtkRenderWindowInteractor> iren;
  iren->SetRenderWindow(renWin.GetPointer());

  ren2->AddViewProp(foheActor.GetPointer());
  ren2->AddViewProp(outlineActor.GetPointer());
  ren2->SetBackground(0.1, 0.2, 0.4);

  normals->Update();

  vtkNew<vtkCubeAxesActor> axes2;
  axes2->SetBounds(normals->GetOutput()->GetBounds());
  axes2->SetXAxisRange(20, 300);
  axes2->SetYAxisRange(-0.01, 0.01);
  axes2->SetCamera(ren2->GetActiveCamera());
  axes2->SetXLabelFormat("%6.1f");
  axes2->SetYLabelFormat("%6.1f");
  axes2->SetZLabelFormat("%6.1f");
  axes2->SetScreenSize(15.0);
  axes2->SetFlyModeToClosestTriad();
  axes2->SetCornerOffset(0.0);
  axes2->SetDrawZGridpolys(1);

  ren2->AddViewProp(axes2.GetPointer());
  renWin->Render();

  int retVal = vtkRegressionTestImage( renWin.GetPointer() );
  if ( retVal == vtkRegressionTester::DO_INTERACTOR)
    {
    iren->Start();
    }

  return !retVal;
}
<commit_msg>Modifying test to verify that attribute properties are indeed used<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    TestCubeAxes3.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

// This example illustrates how one may explicitly specify the range of each
// axes that's used to define the prop, while displaying data with a different
// set of bounds (unlike cubeAxes2.tcl). This example allows you to separate
// the notion of extent of the axes in physical space (bounds) and the extent
// of the values it represents. In other words, you can have the ticks and
// labels show a different range.

#include "vtkBYUReader.h"
#include "vtkCamera.h"
#include "vtkCubeAxesActor.h"
#include "vtkLight.h"
#include "vtkLODActor.h"
#include "vtkNew.h"
#include "vtkOutlineFilter.h"
#include "vtkPolyDataMapper.h"
#include "vtkPolyDataNormals.h"
#include "vtkProperty.h"
#include "vtkRegressionTestImage.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"
#include "vtkTestUtilities.h"

//----------------------------------------------------------------------------
int TestCubeAxesWithZInnerPolys( int argc, char * argv [] )
{
  vtkNew<vtkBYUReader> fohe;
  char* fname = vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/teapot.g");
  fohe->SetGeometryFileName(fname);
  delete [] fname;

  vtkNew<vtkPolyDataNormals> normals;
  normals->SetInputConnection(fohe->GetOutputPort());

  vtkNew<vtkPolyDataMapper> foheMapper;
  foheMapper->SetInputConnection(normals->GetOutputPort());

  vtkNew<vtkLODActor> foheActor;
  foheActor->SetMapper(foheMapper.GetPointer());
  foheActor->GetProperty()->SetDiffuseColor(0.7, 0.3, 0.0);

  vtkNew<vtkOutlineFilter> outline;
  outline->SetInputConnection(normals->GetOutputPort());

  vtkNew<vtkPolyDataMapper> mapOutline;
  mapOutline->SetInputConnection(outline->GetOutputPort());

  vtkNew<vtkActor> outlineActor;
  outlineActor->SetMapper(mapOutline.GetPointer());
  outlineActor->GetProperty()->SetColor(0. ,0. ,0. );

  vtkNew<vtkCamera> camera;
  camera->SetClippingRange(1.0, 100.0);
  camera->SetFocalPoint(0.9, 1.0, 0.0);
  camera->SetPosition(11.63, 6.0, 10.77);

  vtkNew<vtkLight> light;
  light->SetFocalPoint(0.21406, 1.5, 0.0);
  light->SetPosition(8.3761, 4.94858, 4.12505);

  vtkNew<vtkRenderer> ren2;
  ren2->SetActiveCamera(camera.GetPointer());
  ren2->AddLight(light.GetPointer());

  vtkNew<vtkRenderWindow> renWin;
  renWin->SetMultiSamples(0);
  renWin->AddRenderer(ren2.GetPointer());
  renWin->SetWindowName("VTK - Cube Axes custom range");
  renWin->SetSize(600, 600);

  vtkNew<vtkRenderWindowInteractor> iren;
  iren->SetRenderWindow(renWin.GetPointer());

  ren2->AddViewProp(foheActor.GetPointer());
  ren2->AddViewProp(outlineActor.GetPointer());
  ren2->SetBackground(0.1, 0.2, 0.4);

  normals->Update();

  vtkNew<vtkCubeAxesActor> axes2;
  axes2->SetBounds(normals->GetOutput()->GetBounds());
  axes2->SetXAxisRange(20, 300);
  axes2->SetYAxisRange(-0.01, 0.01);
  axes2->SetCamera(ren2->GetActiveCamera());
  axes2->SetXLabelFormat("%6.1f");
  axes2->SetYLabelFormat("%6.1f");
  axes2->SetZLabelFormat("%6.1f");
  axes2->SetScreenSize(15.0);
  axes2->SetFlyModeToClosestTriad();
  axes2->SetCornerOffset(0.0);
  axes2->SetDrawZGridpolys(1);
  axes2->GetZAxesGridpolysProperty()->SetColor(.2, .2, .2);
  axes2->GetZAxesGridpolysProperty()->SetOpacity(.3);

  ren2->AddViewProp(axes2.GetPointer());
  renWin->Render();

  int retVal = vtkRegressionTestImage( renWin.GetPointer() );
  if ( retVal == vtkRegressionTester::DO_INTERACTOR)
    {
    iren->Start();
    }

  return !retVal;
}
<|endoftext|>


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

// Precompiled header file
#include "stdafx.h"
#include "stdafx_editor.h"

// Ui
#include "ui_IntrinsicEdPropertyEditorFloat.h"

IntrinsicEdPropertyEditorFloat::IntrinsicEdPropertyEditorFloat(
    rapidjson::Document* p_Document, rapidjson::Value* p_CurrentProperties,
    rapidjson::Value* p_CurrentProperty, const char* p_PropertyName,
    QWidget* parent)
    : IntrinsicEdPropertyEditorBase(p_Document, p_CurrentProperties,
                                    p_CurrentProperty, p_PropertyName, parent)
{
  _ui.setupUi(this);

  QObject::connect(_ui.value, SIGNAL(valueChanged(double)), this,
                   SLOT(onValueChanged()));

  const rapidjson::Value& prop = *_property;
  if (!prop.HasMember("min") || prop["min"].GetFloat() == -FLT_MAX)
  {
    delete _ui.valueSlider;
    _ui.valueSlider = nullptr;
  }
  else
  {
    const float min = prop["min"].GetFloat();
    const float max = prop["max"].GetFloat();

    _ui.value->setMinimum(min);
    _ui.value->setMaximum(max);

    _ui.valueSlider->setMinimum(0);
    _ui.valueSlider->setMaximum(1000);

    QObject::connect(_ui.valueSlider, SIGNAL(valueChanged(int)), this,
                     SLOT(onSliderValueChanged()));
  }

  updateFromProperty();
}

IntrinsicEdPropertyEditorFloat::~IntrinsicEdPropertyEditorFloat() {}

void IntrinsicEdPropertyEditorFloat::updateFromProperty()
{
  _INTR_ASSERT(_property);
  const rapidjson::Value& prop = *_property;

  if (prop["readOnly"].GetBool())
  {
    _ui.value->setReadOnly(true);
  }

  if (prop["value"].GetFloat() != _ui.value->value() && !_ui.value->hasFocus())
  {
    _ui.value->blockSignals(true);
    _ui.value->setValue(prop["value"].GetFloat());
    _ui.value->blockSignals(false);

    if (_ui.valueSlider)
    {
      const int sliderPos =
          (int)((_ui.value->value() - _ui.value->minimum()) /
                (_ui.value->maximum() - _ui.value->minimum()) * 1000.0f);
      _ui.valueSlider->setValue(sliderPos);
    }
  }

  _ui.propertyTitle->setText(_propertyName.c_str());
}

void IntrinsicEdPropertyEditorFloat::onValueChanged()
{
  _INTR_ASSERT(_property);
  rapidjson::Value& prop = *_property;

  if (prop["value"].GetFloat() != _ui.value->value())
  {
    prop["value"].SetFloat((float)_ui.value->value());

    if (_ui.valueSlider)
    {
      const int sliderPos =
          (int)((_ui.value->value() - _ui.value->minimum()) /
                (_ui.value->maximum() - _ui.value->minimum()) * 1000.0f);
      _ui.valueSlider->setValue(sliderPos);
    }

    emit valueChanged(*_properties);
  }
}

void IntrinsicEdPropertyEditorFloat::onSliderValueChanged()
{
  const float value =
      _ui.value->minimum() + (_ui.valueSlider->value() / 1000.0f) *
                                 (_ui.value->maximum() - _ui.value->minimum());
  _ui.value->setValue(value);
}
<commit_msg>Corectly set the float sliders to the center initially.<commit_after>// Copyright 2017 Benjamin Glatzel
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Precompiled header file
#include "stdafx.h"
#include "stdafx_editor.h"

// Ui
#include "ui_IntrinsicEdPropertyEditorFloat.h"

IntrinsicEdPropertyEditorFloat::IntrinsicEdPropertyEditorFloat(
    rapidjson::Document* p_Document, rapidjson::Value* p_CurrentProperties,
    rapidjson::Value* p_CurrentProperty, const char* p_PropertyName,
    QWidget* parent)
    : IntrinsicEdPropertyEditorBase(p_Document, p_CurrentProperties,
                                    p_CurrentProperty, p_PropertyName, parent)
{
  _ui.setupUi(this);

  QObject::connect(_ui.value, SIGNAL(valueChanged(double)), this,
                   SLOT(onValueChanged()));

  const rapidjson::Value& prop = *_property;
  if (!prop.HasMember("min") || prop["min"].GetFloat() == -FLT_MAX)
  {
    delete _ui.valueSlider;
    _ui.valueSlider = nullptr;
  }
  else
  {
    const float min = prop["min"].GetFloat();
    const float max = prop["max"].GetFloat();

    _ui.value->setMinimum(min);
    _ui.value->setMaximum(max);
    _ui.valueSlider->setValue(500);

    _ui.valueSlider->setMinimum(0);
    _ui.valueSlider->setMaximum(1000);

    QObject::connect(_ui.valueSlider, SIGNAL(valueChanged(int)), this,
                     SLOT(onSliderValueChanged()));
  }

  updateFromProperty();
}

IntrinsicEdPropertyEditorFloat::~IntrinsicEdPropertyEditorFloat() {}

void IntrinsicEdPropertyEditorFloat::updateFromProperty()
{
  _INTR_ASSERT(_property);
  const rapidjson::Value& prop = *_property;

  if (prop["readOnly"].GetBool())
  {
    _ui.value->setReadOnly(true);
  }

  if (prop["value"].GetFloat() != _ui.value->value() && !_ui.value->hasFocus())
  {
    _ui.value->blockSignals(true);
    _ui.value->setValue(prop["value"].GetFloat());
    _ui.value->blockSignals(false);

    if (_ui.valueSlider)
    {
      const int sliderPos =
          (int)((_ui.value->value() - _ui.value->minimum()) /
                (_ui.value->maximum() - _ui.value->minimum()) * 1000.0f);
      _ui.valueSlider->setValue(sliderPos);
    }
  }

  _ui.propertyTitle->setText(_propertyName.c_str());
}

void IntrinsicEdPropertyEditorFloat::onValueChanged()
{
  _INTR_ASSERT(_property);
  rapidjson::Value& prop = *_property;

  if (prop["value"].GetFloat() != _ui.value->value())
  {
    prop["value"].SetFloat((float)_ui.value->value());

    if (_ui.valueSlider)
    {
      const int sliderPos =
          (int)((_ui.value->value() - _ui.value->minimum()) /
                (_ui.value->maximum() - _ui.value->minimum()) * 1000.0f);
      _ui.valueSlider->setValue(sliderPos);
    }

    emit valueChanged(*_properties);
  }
}

void IntrinsicEdPropertyEditorFloat::onSliderValueChanged()
{
  const float value =
      _ui.value->minimum() + (_ui.valueSlider->value() / 1000.0f) *
                                 (_ui.value->maximum() - _ui.value->minimum());
  _ui.value->setValue(value);
}
<|endoftext|>