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

  Library:   CTK

  Copyright (c) Kitware Inc.

  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.txt

  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.

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

// Qt includes
#include <QDebug>
#include <QChildEvent>
#include <QMouseEvent>
#include <QStylePainter>
#include <QStyleOptionGroupBox>
#include <QStyle>

// CTK includes
#include "ctkCollapsibleGroupBox.h"

#if QT_VERSION >= 0x040600
#include <QProxyStyle>

//-----------------------------------------------------------------------------
class ctkCollapsibleGroupBoxStyle:public QProxyStyle
{
  public:

  virtual void drawPrimitive(PrimitiveElement pe, const QStyleOption * opt, QPainter * p, const QWidget * widget = 0) const
  {
    if (pe == QStyle::PE_IndicatorCheckBox)
      {
      const QGroupBox* groupBox= qobject_cast<const QGroupBox*>(widget);
      if (groupBox)
        {
        this->QProxyStyle::drawPrimitive(groupBox->isChecked() ? QStyle::PE_IndicatorArrowDown : QStyle::PE_IndicatorArrowRight, opt, p, widget);
        return;
        }
      }
    this->QProxyStyle::drawPrimitive(pe, opt, p, widget);
  }
};
#else

#endif

//-----------------------------------------------------------------------------
class ctkCollapsibleGroupBoxPrivate
{
  Q_DECLARE_PUBLIC(ctkCollapsibleGroupBox);
protected:
  ctkCollapsibleGroupBox* const q_ptr;
public:
  ctkCollapsibleGroupBoxPrivate(ctkCollapsibleGroupBox& object);
  void init();
  void setChildVisibility(QWidget* childWidget);

  /// Size of the widget for collapsing
  QSize OldSize;
  /// Maximum allowed height
  int   MaxHeight;
  int   CollapsedHeight;

  /// We change the visibility of the chidren in setChildrenVisibility
  /// and we track when the visibility is changed to force it back to possibly
  /// force the child to be hidden. To prevent infinite loop we need to know
  /// who is changing children's visibility.
  bool     ForcingVisibility;
  /// Sometimes the creation of the widget is not done inside setVisible,
  /// as we need to do special processing the first time the button is
  /// setVisible, we track its created state with the variable
  bool     IsStateCreated;
};

//-----------------------------------------------------------------------------
ctkCollapsibleGroupBoxPrivate::ctkCollapsibleGroupBoxPrivate(
  ctkCollapsibleGroupBox& object)
  :q_ptr(&object)
{
  this->ForcingVisibility = false;
  this->IsStateCreated = false;
  this->MaxHeight = 0;
  this->CollapsedHeight = 14;
}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBoxPrivate::init()
{
  Q_Q(ctkCollapsibleGroupBox);
  q->setCheckable(true);
  QObject::connect(q, SIGNAL(toggled(bool)), q, SLOT(expand(bool)));

  this->MaxHeight = q->maximumHeight();
#if QT_VERSION >= 0x040600
  q->setStyle(new ctkCollapsibleGroupBoxStyle);
#else
  this->setStyleSheet(
    "ctkCollapsibleGroupBox::indicator:checked{"
    "image: url(:/Icons/expand-up.png);}"
    "ctkCollapsibleGroupBox::indicator:unchecked{"
    "image: url(:/Icons/expand-down.png);}");
#endif
}
//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBoxPrivate::setChildVisibility(QWidget* childWidget)
{
  Q_Q(ctkCollapsibleGroupBox);
  // Don't hide children while the widget is not yet created (before show() is
  // called). If we hide them (but don't set ExplicitShowHide), they would be
  // shown anyway when they will be created (because ExplicitShowHide is not set).
  // If we set ExplicitShowHide, then calling setVisible(false) on them would
  // be a no (because they are already hidden and ExplicitShowHide is set).
  // So we don't hide/show the children until the widget is created.
  if (!q->testAttribute(Qt::WA_WState_Created))
    {
    return;
    }
  this->ForcingVisibility = true;

  bool visible= !q->collapsed();
  // if the widget has been explicity hidden, then hide it.
  if (childWidget->property("visibilityToParent").isValid()
      && !childWidget->property("visibilityToParent").toBool())
    {
    visible = false;
    }

  childWidget->setVisible(visible);

  // setVisible() has set the ExplicitShowHide flag, restore it as we don't want
  // to make it like it was an explicit visible set because we want
  // to allow the children to be explicitly hidden by the user.
  if ((!childWidget->property("visibilityToParent").isValid() ||
      childWidget->property("visibilityToParent").toBool()))
    {
    childWidget->setAttribute(Qt::WA_WState_ExplicitShowHide, false);
    }
  this->ForcingVisibility = false;
}

//-----------------------------------------------------------------------------
ctkCollapsibleGroupBox::ctkCollapsibleGroupBox(QWidget* _parent)
  :QGroupBox(_parent)
  , d_ptr(new ctkCollapsibleGroupBoxPrivate(*this))
{
  Q_D(ctkCollapsibleGroupBox);
  d->init();
}

//-----------------------------------------------------------------------------
ctkCollapsibleGroupBox::ctkCollapsibleGroupBox(const QString& title, QWidget* _parent)
  :QGroupBox(title, _parent)
  , d_ptr(new ctkCollapsibleGroupBoxPrivate(*this))
{
  Q_D(ctkCollapsibleGroupBox);
  d->init();
}

//-----------------------------------------------------------------------------
ctkCollapsibleGroupBox::~ctkCollapsibleGroupBox()
{

}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::setCollapsedHeight(int heightInPixels)
{
  Q_D(ctkCollapsibleGroupBox);
  d->CollapsedHeight = heightInPixels;
}

//-----------------------------------------------------------------------------
int ctkCollapsibleGroupBox::collapsedHeight()const
{
  Q_D(const ctkCollapsibleGroupBox);
  return d->CollapsedHeight;
}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::expand(bool _expand)
{
  Q_D(ctkCollapsibleGroupBox);
  if (!_expand)
    {
    d->OldSize = this->size();
    }

  // Update the visibility of all the children
  // We can't use findChildren as it would return the grandchildren
  foreach(QObject* childObject, this->children())
    {
    if (childObject->isWidgetType())
      {
      d->setChildVisibility(qobject_cast<QWidget*>(childObject));
      }
    }

  if (_expand)
    {
    this->setMaximumHeight(d->MaxHeight);
    this->resize(d->OldSize);
    }
  else
    {
    d->MaxHeight = this->maximumHeight();
    QStyleOptionGroupBox option;
    this->initStyleOption(&option);
    QRect labelRect = this->style()->subControlRect(
      QStyle::CC_GroupBox, &option, QStyle::SC_GroupBoxLabel, this);
    this->setMaximumHeight(labelRect.height() + d->CollapsedHeight);
    }
}

#if QT_VERSION < 0x040600
//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::paintEvent(QPaintEvent* e)
{
  this->QGroupBox::paintEvent(e);

  QStylePainter paint(this);
  QStyleOptionGroupBox option;
  initStyleOption(&option);
  option.activeSubControls &= ~QStyle::SC_GroupBoxCheckBox;
  paint.drawComplexControl(QStyle::CC_GroupBox, option);

}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::mousePressEvent(QMouseEvent *event)
{
    if (event->button() != Qt::LeftButton) {
        event->ignore();
        return;
    }
    // no animation
}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() != Qt::LeftButton) {
        event->ignore();
        return;
    }

    QStyleOptionGroupBox box;
    initStyleOption(&box);
    box.activeSubControls &= !QStyle::SC_GroupBoxCheckBox;
    QStyle::SubControl released = style()->hitTestComplexControl(QStyle::CC_GroupBox, &box,
                                                                 event->pos(), this);
    bool toggle = this->isCheckable() && (released == QStyle::SC_GroupBoxLabel
                                   || released == QStyle::SC_GroupBoxCheckBox);
    if (toggle)
      {
      this->setChecked(!this->isChecked());
      }
}

#endif

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::childEvent(QChildEvent* c)
{
  Q_D(ctkCollapsibleGroupBox);
  QObject* child = c->child();
  if (c && c->type() == QEvent::ChildAdded &&
      child && child->isWidgetType())
    {
    QWidget *childWidget = qobject_cast<QWidget*>(c->child());
    // Handle the case where the child has already it's visibility set before
    // being added to the widget
    if (childWidget->testAttribute(Qt::WA_WState_ExplicitShowHide) &&
        childWidget->testAttribute(Qt::WA_WState_Hidden))
      {
      // if the widget has explicitly set to hidden, then mark it as such
      childWidget->setProperty("visibilityToParent", false);
      }
    // We want to catch all the child's Show/Hide events.
    child->installEventFilter(this);
    // If the child is added while ctkCollapsibleButton is collapsed, then we
    // need to hide the child.
    d->setChildVisibility(childWidget);
    }
  this->QGroupBox::childEvent(c);
}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::setVisible(bool show)
{
  Q_D(ctkCollapsibleGroupBox);
  // calling QWidget::setVisible() on ctkCollapsibleGroupBox will eventually
  // call QWidget::showChildren() or hideChildren() which will generate
  // ShowToParent/HideToParent events but we want to ignore that case in
  // eventFilter().
  d->ForcingVisibility = true;
  this->QGroupBox::setVisible(show);
  d->ForcingVisibility = false;
  // We have been ignoring setChildVisibility() while the collapsible button
  // is not yet created, now that it is created, ensure that the children
  // are correctly shown/hidden depending on their explicit visibility and
  // the collapsed property of the button.
  if (!d->IsStateCreated && this->testAttribute(Qt::WA_WState_Created))
    {
    d->IsStateCreated = true;
    foreach(QObject* child, this->children())
      {
      QWidget* childWidget = qobject_cast<QWidget*>(child);
      if (childWidget)
        {
        d->setChildVisibility(childWidget);
        }
      }
    }
}

//-----------------------------------------------------------------------------
bool ctkCollapsibleGroupBox::eventFilter(QObject* child, QEvent* e)
{
  Q_D(ctkCollapsibleGroupBox);
  Q_ASSERT(child && e);
  // Make sure the Show/QHide events are not generated by one of our
  // ctkCollapsibleButton function.
  if (d->ForcingVisibility)
    {
    return false;
    }
  // When we are here, it's because somewhere (not in ctkCollapsibleButton),
  // someone explicitly called setVisible() on a child widget.
  // If the collapsible button is collapsed/closed, then even if someone
  // request the widget to be visible, we force it back to be hidden because
  // they meant to be hidden to its parent, the collapsible button. However the
  // child will later be shown when the button will be expanded/opened.
  // On the other hand, if the user explicitly hide the child when the button
  // is collapsed/closed, then we want to keep it hidden next time the
  // collapsible button is expanded/opened.
  if (e->type() == QEvent::ShowToParent)
    {
    child->setProperty("visibilityToParent", true);
    Q_ASSERT(qobject_cast<QWidget*>(child));
    // force the widget to be hidden if the button is collapsed.
    d->setChildVisibility(qobject_cast<QWidget*>(child));
    }
  else if(e->type() == QEvent::HideToParent)
    {
    // we don't need to force the widget to be visible here.
    child->setProperty("visibilityToParent", false);
    }
  return this->QGroupBox::eventFilter(child, e);
}
<commit_msg>Fix title height of ctkCollapsibleGroupBox<commit_after>/*=========================================================================

  Library:   CTK

  Copyright (c) Kitware Inc.

  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.txt

  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.

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

// Qt includes
#include <QDebug>
#include <QChildEvent>
#include <QMouseEvent>
#include <QStylePainter>
#include <QStyleOptionGroupBox>
#include <QStyle>

// CTK includes
#include "ctkCollapsibleGroupBox.h"

#if QT_VERSION >= 0x040600
#include <QProxyStyle>

//-----------------------------------------------------------------------------
class ctkCollapsibleGroupBoxStyle:public QProxyStyle
{
  public:
  ctkCollapsibleGroupBoxStyle(QStyle* style = 0) : QProxyStyle(style)
  {
  }
  virtual void drawPrimitive(PrimitiveElement pe, const QStyleOption * opt, QPainter * p, const QWidget * widget = 0) const
  {
    if (pe == QStyle::PE_IndicatorCheckBox)
      {
      const ctkCollapsibleGroupBox* groupBox= qobject_cast<const ctkCollapsibleGroupBox*>(widget);
      if (groupBox)
        {
        this->QProxyStyle::drawPrimitive(groupBox->isChecked() ? QStyle::PE_IndicatorArrowDown : QStyle::PE_IndicatorArrowRight, opt, p, widget);
        return;
        }
      }
    this->QProxyStyle::drawPrimitive(pe, opt, p, widget);
  }
  virtual int pixelMetric(PixelMetric metric, const QStyleOption * option, const QWidget * widget) const
  {
    if (metric == QStyle::PM_IndicatorHeight)
      {
      const ctkCollapsibleGroupBox* groupBox= qobject_cast<const ctkCollapsibleGroupBox*>(widget);
      if (groupBox)
        {
        return groupBox->fontMetrics().height();
        }
      }
    return this->QProxyStyle::pixelMetric(metric, option, widget);
  }
};
#else

#endif

//-----------------------------------------------------------------------------
class ctkCollapsibleGroupBoxPrivate
{
  Q_DECLARE_PUBLIC(ctkCollapsibleGroupBox);
protected:
  ctkCollapsibleGroupBox* const q_ptr;
public:
  ctkCollapsibleGroupBoxPrivate(ctkCollapsibleGroupBox& object);
  void init();
  void setChildVisibility(QWidget* childWidget);

  /// Size of the widget for collapsing
  QSize OldSize;
  /// Maximum allowed height
  int   MaxHeight;
  int   CollapsedHeight;

  /// We change the visibility of the chidren in setChildrenVisibility
  /// and we track when the visibility is changed to force it back to possibly
  /// force the child to be hidden. To prevent infinite loop we need to know
  /// who is changing children's visibility.
  bool     ForcingVisibility;
  /// Sometimes the creation of the widget is not done inside setVisible,
  /// as we need to do special processing the first time the button is
  /// setVisible, we track its created state with the variable
  bool     IsStateCreated;
};

//-----------------------------------------------------------------------------
ctkCollapsibleGroupBoxPrivate::ctkCollapsibleGroupBoxPrivate(
  ctkCollapsibleGroupBox& object)
  :q_ptr(&object)
{
  this->ForcingVisibility = false;
  this->IsStateCreated = false;
  this->MaxHeight = 0;
  this->CollapsedHeight = 14;
}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBoxPrivate::init()
{
  Q_Q(ctkCollapsibleGroupBox);
  q->setCheckable(true);
  QObject::connect(q, SIGNAL(toggled(bool)), q, SLOT(expand(bool)));

  this->MaxHeight = q->maximumHeight();
#if QT_VERSION >= 0x040600
  q->setStyle(new ctkCollapsibleGroupBoxStyle(q->style()));
#else
  this->setStyleSheet(
    "ctkCollapsibleGroupBox::indicator:checked{"
    "image: url(:/Icons/expand-up.png);}"
    "ctkCollapsibleGroupBox::indicator:unchecked{"
    "image: url(:/Icons/expand-down.png);}");
#endif
}
//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBoxPrivate::setChildVisibility(QWidget* childWidget)
{
  Q_Q(ctkCollapsibleGroupBox);
  // Don't hide children while the widget is not yet created (before show() is
  // called). If we hide them (but don't set ExplicitShowHide), they would be
  // shown anyway when they will be created (because ExplicitShowHide is not set).
  // If we set ExplicitShowHide, then calling setVisible(false) on them would
  // be a no (because they are already hidden and ExplicitShowHide is set).
  // So we don't hide/show the children until the widget is created.
  if (!q->testAttribute(Qt::WA_WState_Created))
    {
    return;
    }
  this->ForcingVisibility = true;

  bool visible= !q->collapsed();
  // if the widget has been explicity hidden, then hide it.
  if (childWidget->property("visibilityToParent").isValid()
      && !childWidget->property("visibilityToParent").toBool())
    {
    visible = false;
    }

  childWidget->setVisible(visible);

  // setVisible() has set the ExplicitShowHide flag, restore it as we don't want
  // to make it like it was an explicit visible set because we want
  // to allow the children to be explicitly hidden by the user.
  if ((!childWidget->property("visibilityToParent").isValid() ||
      childWidget->property("visibilityToParent").toBool()))
    {
    childWidget->setAttribute(Qt::WA_WState_ExplicitShowHide, false);
    }
  this->ForcingVisibility = false;
}

//-----------------------------------------------------------------------------
ctkCollapsibleGroupBox::ctkCollapsibleGroupBox(QWidget* _parent)
  :QGroupBox(_parent)
  , d_ptr(new ctkCollapsibleGroupBoxPrivate(*this))
{
  Q_D(ctkCollapsibleGroupBox);
  d->init();
}

//-----------------------------------------------------------------------------
ctkCollapsibleGroupBox::ctkCollapsibleGroupBox(const QString& title, QWidget* _parent)
  :QGroupBox(title, _parent)
  , d_ptr(new ctkCollapsibleGroupBoxPrivate(*this))
{
  Q_D(ctkCollapsibleGroupBox);
  d->init();
}

//-----------------------------------------------------------------------------
ctkCollapsibleGroupBox::~ctkCollapsibleGroupBox()
{

}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::setCollapsedHeight(int heightInPixels)
{
  Q_D(ctkCollapsibleGroupBox);
  d->CollapsedHeight = heightInPixels;
}

//-----------------------------------------------------------------------------
int ctkCollapsibleGroupBox::collapsedHeight()const
{
  Q_D(const ctkCollapsibleGroupBox);
  return d->CollapsedHeight;
}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::expand(bool _expand)
{
  Q_D(ctkCollapsibleGroupBox);
  if (!_expand)
    {
    d->OldSize = this->size();
    }

  // Update the visibility of all the children
  // We can't use findChildren as it would return the grandchildren
  foreach(QObject* childObject, this->children())
    {
    if (childObject->isWidgetType())
      {
      d->setChildVisibility(qobject_cast<QWidget*>(childObject));
      }
    }

  if (_expand)
    {
    this->setMaximumHeight(d->MaxHeight);
    this->resize(d->OldSize);
    }
  else
    {
    d->MaxHeight = this->maximumHeight();
    QStyleOptionGroupBox option;
    this->initStyleOption(&option);
    QRect labelRect = this->style()->subControlRect(
      QStyle::CC_GroupBox, &option, QStyle::SC_GroupBoxLabel, this);
    this->setMaximumHeight(labelRect.height() + d->CollapsedHeight);
    }
}

#if QT_VERSION < 0x040600
//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::paintEvent(QPaintEvent* e)
{
  this->QGroupBox::paintEvent(e);

  QStylePainter paint(this);
  QStyleOptionGroupBox option;
  initStyleOption(&option);
  option.activeSubControls &= ~QStyle::SC_GroupBoxCheckBox;
  paint.drawComplexControl(QStyle::CC_GroupBox, option);

}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::mousePressEvent(QMouseEvent *event)
{
    if (event->button() != Qt::LeftButton) {
        event->ignore();
        return;
    }
    // no animation
}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() != Qt::LeftButton) {
        event->ignore();
        return;
    }

    QStyleOptionGroupBox box;
    initStyleOption(&box);
    box.activeSubControls &= !QStyle::SC_GroupBoxCheckBox;
    QStyle::SubControl released = style()->hitTestComplexControl(QStyle::CC_GroupBox, &box,
                                                                 event->pos(), this);
    bool toggle = this->isCheckable() && (released == QStyle::SC_GroupBoxLabel
                                   || released == QStyle::SC_GroupBoxCheckBox);
    if (toggle)
      {
      this->setChecked(!this->isChecked());
      }
}

#endif

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::childEvent(QChildEvent* c)
{
  Q_D(ctkCollapsibleGroupBox);
  QObject* child = c->child();
  if (c && c->type() == QEvent::ChildAdded &&
      child && child->isWidgetType())
    {
    QWidget *childWidget = qobject_cast<QWidget*>(c->child());
    // Handle the case where the child has already it's visibility set before
    // being added to the widget
    if (childWidget->testAttribute(Qt::WA_WState_ExplicitShowHide) &&
        childWidget->testAttribute(Qt::WA_WState_Hidden))
      {
      // if the widget has explicitly set to hidden, then mark it as such
      childWidget->setProperty("visibilityToParent", false);
      }
    // We want to catch all the child's Show/Hide events.
    child->installEventFilter(this);
    // If the child is added while ctkCollapsibleButton is collapsed, then we
    // need to hide the child.
    d->setChildVisibility(childWidget);
    }
  this->QGroupBox::childEvent(c);
}

//-----------------------------------------------------------------------------
void ctkCollapsibleGroupBox::setVisible(bool show)
{
  Q_D(ctkCollapsibleGroupBox);
  // calling QWidget::setVisible() on ctkCollapsibleGroupBox will eventually
  // call QWidget::showChildren() or hideChildren() which will generate
  // ShowToParent/HideToParent events but we want to ignore that case in
  // eventFilter().
  d->ForcingVisibility = true;
  this->QGroupBox::setVisible(show);
  d->ForcingVisibility = false;
  // We have been ignoring setChildVisibility() while the collapsible button
  // is not yet created, now that it is created, ensure that the children
  // are correctly shown/hidden depending on their explicit visibility and
  // the collapsed property of the button.
  if (!d->IsStateCreated && this->testAttribute(Qt::WA_WState_Created))
    {
    d->IsStateCreated = true;
    foreach(QObject* child, this->children())
      {
      QWidget* childWidget = qobject_cast<QWidget*>(child);
      if (childWidget)
        {
        d->setChildVisibility(childWidget);
        }
      }
    }
}

//-----------------------------------------------------------------------------
bool ctkCollapsibleGroupBox::eventFilter(QObject* child, QEvent* e)
{
  Q_D(ctkCollapsibleGroupBox);
  Q_ASSERT(child && e);
  // Make sure the Show/QHide events are not generated by one of our
  // ctkCollapsibleButton function.
  if (d->ForcingVisibility)
    {
    return false;
    }
  // When we are here, it's because somewhere (not in ctkCollapsibleButton),
  // someone explicitly called setVisible() on a child widget.
  // If the collapsible button is collapsed/closed, then even if someone
  // request the widget to be visible, we force it back to be hidden because
  // they meant to be hidden to its parent, the collapsible button. However the
  // child will later be shown when the button will be expanded/opened.
  // On the other hand, if the user explicitly hide the child when the button
  // is collapsed/closed, then we want to keep it hidden next time the
  // collapsible button is expanded/opened.
  if (e->type() == QEvent::ShowToParent)
    {
    child->setProperty("visibilityToParent", true);
    Q_ASSERT(qobject_cast<QWidget*>(child));
    // force the widget to be hidden if the button is collapsed.
    d->setChildVisibility(qobject_cast<QWidget*>(child));
    }
  else if(e->type() == QEvent::HideToParent)
    {
    // we don't need to force the widget to be visible here.
    child->setProperty("visibilityToParent", false);
    }
  return this->QGroupBox::eventFilter(child, e);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2018 Dubalu LLC. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "exception.h"

#include "config.h"           // for XAPIAND_TRACEBACKS

#include <cstdlib>            // for free
#include <cstring>            // for strtok_r
#include <cxxabi.h>           // for abi::__cxa_demangle
#include <dlfcn.h>            // for dladdr

#ifdef HAVE_EXECINFO_H
#include <execinfo.h>         // for backtrace
#else
static inline int backtrace(void**, int) { return 0; }
#endif

#include "likely.h"

#define BUFFER_SIZE 1024

#ifdef __APPLE__
#include <util.h>             // for forkpty
#include <unistd.h>           // for execlp
#include <termios.h>          // for termios, cfmakeraw
#include <sys/select.h>       // for select
/* Use `atos` to do symbol lookup, can lookup non-dynamic symbols and also line
 * numbers. This function is more complicated than you'd expect because `atos`
 * doesn't flush after each line, so plain pipe() or socketpair() won't work
 * until we close the write side. But the whole point is we want to keep `atos`
 * around so we don't have to reprocess the symbol table over and over. What we
 * wind up doing is using `forkpty()` to make a new pseudoterminal for atos to
 * run in, and thus will use line-buffering for stdout, and then we can get
 * each line.
 */
static std::string
atos(const void* address)
{
	static std::mutex mtx;
	std::lock_guard lk(mtx);

	char tmp[20];
	static int fd = -1;
	if (fd == -1) {
		Dl_info info;
		memset(&info, 0, sizeof(Dl_info));
		if (dladdr(reinterpret_cast<const void*>(&atos), &info) == 0) {
			perror("Could not get base address for `atos`.");
			return "";
		}

		struct termios term_opts;
		cfmakeraw(&term_opts);  // have to set this first, otherwise queries echo until child kicks in
		pid_t childpid;
		if unlikely((childpid = forkpty(&fd, nullptr, &term_opts, nullptr)) < 0) {
			perror("Could not forkpty for `atos` call.");
			return "";
		}

		if (childpid == 0) {
			snprintf(tmp, sizeof(tmp), "%p", static_cast<const void *>(info.dli_fbase));
			execlp("/usr/bin/atos", "atos", "-o", info.dli_fname, "-l", tmp, nullptr);
			fprintf(stderr,"Could not exec `atos` for stack trace!\n");
			exit(1);
		}

		int size = snprintf(tmp, sizeof(tmp), "%p\n", address);
		write(fd, tmp, size);

		// atos can take a while to parse symbol table on first request, which
		// is why we leave it running if we see a delay, explain what's going on...
		fd_set fds;
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
		struct timeval tv = {3, 0};
		int err = select(fd + 1, &fds, nullptr, nullptr, &tv);
		if unlikely(err < 0) {
			perror("Generating... first call takes some time for `atos` to cache the symbol table.");
		} else if (err == 0) {  // timeout
			fprintf(stderr, "Generating... first call takes some time for `atos` to cache the symbol table.\n");
		}
	} else {
		int size = snprintf(tmp, sizeof(tmp), "%p\n", address);
		write(fd, tmp, size);
	}

	const unsigned int MAXLINE = 1024;
	char line[MAXLINE];
	char c = '\0';
	size_t nread = 0;
	while (c != '\n' && nread < MAXLINE) {
		if unlikely(read(fd, &c, 1) <= 0) {
			perror("Lost `atos` connection.");
			close(fd);
			fd = -1;
			return "";
		}
		if (c != '\n') {
			line[nread++] = c;
		}
	}
	if (nread < MAXLINE) {
		return std::string(line, nread);
	}
	fprintf(stderr, "Line read from `atos` was too long.\n");
	return "";
}
#else
static inline std::string
atos(const void*)
{
	return "";
}
#endif

std::string
traceback(const char* function, const char* filename, int line, const std::vector<void*>& callstack, int skip = 1)
{
	char tmp[20];

	std::string tb = "\n== Traceback (most recent call first): ";
	tb.append(filename);
	tb.push_back(':');
	tb.append(std::to_string(line));
	tb.append(" at ");
	tb.append(function);

	int frames = callstack.size();

	if (frames == 0) {
		tb.append(":\n    <empty traceback>");
		return tb;
	}

	if (frames < 2) {
		tb.append(":\n    <no traceback>");
		return tb;
	}

	tb.push_back(':');

	// Iterate over the callstack. Skip the first, it is the address of this function.
	std::string result;
	for (int i = skip; i < frames; ++i) {
		auto address = callstack[i];

		result.assign(std::to_string(frames - i - 1));
		result.push_back(' ');

		auto address_string = atos(address);
		if (address_string.size() > 2 && address_string.compare(0, 2, "0x") != 0) {
			result.append(address_string);
		} else {
			Dl_info info;
			memset(&info, 0, sizeof(Dl_info));
			if (dladdr(address, &info) != 0) {
				// Address:
				snprintf(tmp, sizeof(tmp), "%p ", address);
				result.append(tmp);
				// Symbol name:
				if (info.dli_sname != nullptr) {
					int status = 0;
					char* unmangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
					if (status != 0) {
						result.append(info.dli_sname);
					} else {
						try {
							result.append(unmangled);
							free(unmangled);
						} catch(...) {
							free(unmangled);
							throw;
						}
					}
				} else {
					result.append("[unknown symbol]");
				}
				// Offset:
				snprintf(tmp, sizeof(tmp), " + %zu", static_cast<const char*>(address) - static_cast<const char*>(info.dli_saddr));
				result.append(tmp);
			} else {
				snprintf(tmp, sizeof(tmp), "%p [unknown symbol]", address);
				result.append(tmp);
			}
		}
		tb.append("\n    ");
		tb.append(result);
	}

	return tb;
}

std::string
traceback(const char* function, const char* filename, int line, int skip)
{
	std::vector<void*> callstack;

	// retrieve current stack addresses
	callstack.resize(128);
	callstack.resize(backtrace(callstack.data(), callstack.size()));
	callstack.shrink_to_fit();

	auto tb = traceback(function, filename, line, callstack, skip);

	return tb;
}

extern "C" void
__assert_tb(const char* function, const char* filename, unsigned int line, const char* expression)
{
	(void)fprintf(stderr, "Assertion failed: %s, function %s, file %s, line %u.%s\n",
		expression, function, filename, line, traceback(function, filename, line, 2).c_str());
	abort();
}

BaseException::BaseException()
	: line{0}
{ }

BaseException::BaseException(const BaseException& exc)
	: type{exc.type},
	  function{exc.function},
	  filename{exc.filename},
	  line{exc.line},
	  callstack{exc.callstack},
	  message{exc.message},
	  context{exc.context},
	  traceback{exc.traceback}
{
}

BaseException::BaseException(BaseException&& exc)
	: type{std::move(exc.type)},
	  function{std::move(exc.function)},
	  filename{std::move(exc.filename)},
	  line{std::move(exc.line)},
	  callstack{std::move(exc.callstack)},
	  message{std::move(exc.message)},
	  context{std::move(exc.context)},
	  traceback{std::move(exc.traceback)}
{ }

BaseException::BaseException(const BaseException* exc)
	: BaseException(exc != nullptr ? *exc : BaseException())
{ }

BaseException::BaseException(BaseException::private_ctor, const BaseException& exc, const char *function_, const char *filename_, int line_, const char* type, std::string_view format, VFPRINTF_ARGS args)
	: type(type),
	  function(function_),
	  filename(filename_),
	  line(line_),
	  message(VSPRINTF(format, args))
{
	if (!exc.type.empty()) {
		function = exc.function;
		filename = exc.filename;
		line = exc.line;
		callstack = exc.callstack;
	} else {
#ifdef XAPIAND_TRACEBACKS
		// retrieve current stack addresses
		callstack.resize(128);
		callstack.resize(backtrace(callstack.data(), callstack.size()));
		callstack.shrink_to_fit();
#endif
	}
}

const char*
BaseException::get_message() const
{
	if (message.empty()) {
		message.assign(type);
	}
	return message.c_str();
}

const char*
BaseException::get_context() const
{
	if (context.empty()) {
		context.append(filename);
		context.push_back(':');
		context.append(std::to_string(line));
		context.append(" at ");
		context.append(function);
		context.append(": ");
		context.append(get_message());
	}
	return context.c_str();
}

const char*
BaseException::get_traceback() const
{
	if (traceback.empty()) {
		traceback = ::traceback(function, filename, line, callstack);
	}
	return traceback.c_str();
}
<commit_msg>Exceptions with traceback forced define as comment<commit_after>/*
 * Copyright (C) 2015-2018 Dubalu LLC. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "exception.h"

#include "config.h"           // for XAPIAND_TRACEBACKS

#include <cstdlib>            // for free
#include <cstring>            // for strtok_r
#include <cxxabi.h>           // for abi::__cxa_demangle
#include <dlfcn.h>            // for dladdr

#ifdef HAVE_EXECINFO_H
#include <execinfo.h>         // for backtrace
#else
static inline int backtrace(void**, int) { return 0; }
#endif

#include "likely.h"

// #ifndef XAPIAND_TRACEBACKS
// #define XAPIAND_TRACEBACKS 1
// #endif

#define BUFFER_SIZE 1024

#ifdef __APPLE__
#include <util.h>             // for forkpty
#include <unistd.h>           // for execlp
#include <termios.h>          // for termios, cfmakeraw
#include <sys/select.h>       // for select
/* Use `atos` to do symbol lookup, can lookup non-dynamic symbols and also line
 * numbers. This function is more complicated than you'd expect because `atos`
 * doesn't flush after each line, so plain pipe() or socketpair() won't work
 * until we close the write side. But the whole point is we want to keep `atos`
 * around so we don't have to reprocess the symbol table over and over. What we
 * wind up doing is using `forkpty()` to make a new pseudoterminal for atos to
 * run in, and thus will use line-buffering for stdout, and then we can get
 * each line.
 */
static std::string
atos(const void* address)
{
	static std::mutex mtx;
	std::lock_guard lk(mtx);

	char tmp[20];
	static int fd = -1;
	if (fd == -1) {
		Dl_info info;
		memset(&info, 0, sizeof(Dl_info));
		if (dladdr(reinterpret_cast<const void*>(&atos), &info) == 0) {
			perror("Could not get base address for `atos`.");
			return "";
		}

		struct termios term_opts;
		cfmakeraw(&term_opts);  // have to set this first, otherwise queries echo until child kicks in
		pid_t childpid;
		if unlikely((childpid = forkpty(&fd, nullptr, &term_opts, nullptr)) < 0) {
			perror("Could not forkpty for `atos` call.");
			return "";
		}

		if (childpid == 0) {
			snprintf(tmp, sizeof(tmp), "%p", static_cast<const void *>(info.dli_fbase));
			execlp("/usr/bin/atos", "atos", "-o", info.dli_fname, "-l", tmp, nullptr);
			fprintf(stderr,"Could not exec `atos` for stack trace!\n");
			exit(1);
		}

		int size = snprintf(tmp, sizeof(tmp), "%p\n", address);
		write(fd, tmp, size);

		// atos can take a while to parse symbol table on first request, which
		// is why we leave it running if we see a delay, explain what's going on...
		fd_set fds;
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
		struct timeval tv = {3, 0};
		int err = select(fd + 1, &fds, nullptr, nullptr, &tv);
		if unlikely(err < 0) {
			perror("Generating... first call takes some time for `atos` to cache the symbol table.");
		} else if (err == 0) {  // timeout
			fprintf(stderr, "Generating... first call takes some time for `atos` to cache the symbol table.\n");
		}
	} else {
		int size = snprintf(tmp, sizeof(tmp), "%p\n", address);
		write(fd, tmp, size);
	}

	const unsigned int MAXLINE = 1024;
	char line[MAXLINE];
	char c = '\0';
	size_t nread = 0;
	while (c != '\n' && nread < MAXLINE) {
		if unlikely(read(fd, &c, 1) <= 0) {
			perror("Lost `atos` connection.");
			close(fd);
			fd = -1;
			return "";
		}
		if (c != '\n') {
			line[nread++] = c;
		}
	}
	if (nread < MAXLINE) {
		return std::string(line, nread);
	}
	fprintf(stderr, "Line read from `atos` was too long.\n");
	return "";
}
#else
static inline std::string
atos(const void*)
{
	return "";
}
#endif

std::string
traceback(const char* function, const char* filename, int line, const std::vector<void*>& callstack, int skip = 1)
{
	char tmp[20];

	std::string tb = "\n== Traceback (most recent call first): ";
	tb.append(filename);
	tb.push_back(':');
	tb.append(std::to_string(line));
	tb.append(" at ");
	tb.append(function);

	int frames = callstack.size();

	if (frames == 0) {
		tb.append(":\n    <empty traceback>");
		return tb;
	}

	if (frames < 2) {
		tb.append(":\n    <no traceback>");
		return tb;
	}

	tb.push_back(':');

	// Iterate over the callstack. Skip the first, it is the address of this function.
	std::string result;
	for (int i = skip; i < frames; ++i) {
		auto address = callstack[i];

		result.assign(std::to_string(frames - i - 1));
		result.push_back(' ');

		auto address_string = atos(address);
		if (address_string.size() > 2 && address_string.compare(0, 2, "0x") != 0) {
			result.append(address_string);
		} else {
			Dl_info info;
			memset(&info, 0, sizeof(Dl_info));
			if (dladdr(address, &info) != 0) {
				// Address:
				snprintf(tmp, sizeof(tmp), "%p ", address);
				result.append(tmp);
				// Symbol name:
				if (info.dli_sname != nullptr) {
					int status = 0;
					char* unmangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
					if (status != 0) {
						result.append(info.dli_sname);
					} else {
						try {
							result.append(unmangled);
							free(unmangled);
						} catch(...) {
							free(unmangled);
							throw;
						}
					}
				} else {
					result.append("[unknown symbol]");
				}
				// Offset:
				snprintf(tmp, sizeof(tmp), " + %zu", static_cast<const char*>(address) - static_cast<const char*>(info.dli_saddr));
				result.append(tmp);
			} else {
				snprintf(tmp, sizeof(tmp), "%p [unknown symbol]", address);
				result.append(tmp);
			}
		}
		tb.append("\n    ");
		tb.append(result);
	}

	return tb;
}

std::string
traceback(const char* function, const char* filename, int line, int skip)
{
	std::vector<void*> callstack;

	// retrieve current stack addresses
	callstack.resize(128);
	callstack.resize(backtrace(callstack.data(), callstack.size()));
	callstack.shrink_to_fit();

	auto tb = traceback(function, filename, line, callstack, skip);

	return tb;
}

extern "C" void
__assert_tb(const char* function, const char* filename, unsigned int line, const char* expression)
{
	(void)fprintf(stderr, "Assertion failed: %s, function %s, file %s, line %u.%s\n",
		expression, function, filename, line, traceback(function, filename, line, 2).c_str());
	abort();
}

BaseException::BaseException()
	: line{0}
{ }

BaseException::BaseException(const BaseException& exc)
	: type{exc.type},
	  function{exc.function},
	  filename{exc.filename},
	  line{exc.line},
	  callstack{exc.callstack},
	  message{exc.message},
	  context{exc.context},
	  traceback{exc.traceback}
{
}

BaseException::BaseException(BaseException&& exc)
	: type{std::move(exc.type)},
	  function{std::move(exc.function)},
	  filename{std::move(exc.filename)},
	  line{std::move(exc.line)},
	  callstack{std::move(exc.callstack)},
	  message{std::move(exc.message)},
	  context{std::move(exc.context)},
	  traceback{std::move(exc.traceback)}
{ }

BaseException::BaseException(const BaseException* exc)
	: BaseException(exc != nullptr ? *exc : BaseException())
{ }

BaseException::BaseException(BaseException::private_ctor, const BaseException& exc, const char *function_, const char *filename_, int line_, const char* type, std::string_view format, VFPRINTF_ARGS args)
	: type(type),
	  function(function_),
	  filename(filename_),
	  line(line_),
	  message(VSPRINTF(format, args))
{
	if (!exc.type.empty()) {
		function = exc.function;
		filename = exc.filename;
		line = exc.line;
		callstack = exc.callstack;
	} else {
#ifdef XAPIAND_TRACEBACKS
		// retrieve current stack addresses
		callstack.resize(128);
		callstack.resize(backtrace(callstack.data(), callstack.size()));
		callstack.shrink_to_fit();
#endif
	}
}

const char*
BaseException::get_message() const
{
	if (message.empty()) {
		message.assign(type);
	}
	return message.c_str();
}

const char*
BaseException::get_context() const
{
	if (context.empty()) {
		context.append(filename);
		context.push_back(':');
		context.append(std::to_string(line));
		context.append(" at ");
		context.append(function);
		context.append(": ");
		context.append(get_message());
	}
	return context.c_str();
}

const char*
BaseException::get_traceback() const
{
	if (traceback.empty()) {
		traceback = ::traceback(function, filename, line, callstack);
	}
	return traceback.c_str();
}
<|endoftext|>


<commit_before>#pragma once

#include "fwd.hpp"

#include <boost/variant.hpp>
#include <boost/filesystem/path.hpp>

#include <iosfwd>
#include <memory>
#include <type_traits>
#include <vector>
#include <unordered_map>
#include <boost/functional/hash.hpp>

namespace configure {

	// Encapsulate a map where keys are strings and values are strings, path,
	// bool, or integers.
	//
	// The case of keys is ignored and characters '-' and '_' are considered
	// the same. Only ascii characters are accepted.
	class Environ
	{
	public:

	public:
		template<typename T> struct const_ref { typedef T const& type; };
		template<typename T> struct extract;
		enum class Kind {
			string,
			path,
			boolean,
			integer,
			list,
			none,
		};
		typedef boost::make_recursive_variant<
			std::string,
			boost::filesystem::path,
			bool,
			int,
			std::vector<boost::recursive_variant_>
		>::type Value;

	private:
		template<typename Char>
		static inline Char normalize_char(Char c)
		{
			static std::locale const locale;
			if (c == '-') return '_';
			return std::toupper(c, locale);
		}

		struct Hash
		{
			template<typename String>
			size_t operator ()(String const& value) const
			{
				size_t seed = 0;
				for (auto c: value)
					boost::hash_combine(seed, Environ::normalize_char(c));
				return seed;
			}
		};
		struct Equals
		{
			// My guess is that equals is called only when hashes matches
			// so the strings are likely to be equal.
			template<typename String>
			bool operator() (String const& lhs, String const& rhs) const
			{
				auto p1 = lhs.c_str();
				auto p2 = rhs.c_str();
				while (*p1 != '\0')
				{
					if (Environ::normalize_char(*p1) != Environ::normalize_char(*p2))
						return false;
					p1 += 1;
					p2 += 1;
				}
				return *p2 == '\0';
			}
		};
		typedef std::unordered_map<std::string, Value, Hash, Equals> Values;
		Values _values;

	public:
		Environ();
		Environ(Environ const& other);
		~Environ();

		bool has(std::string const& key) const;
		Kind kind(std::string const& key) const;
		std::string as_string(std::string const& key) const;

		// Returns an existing value of type T or throws if the key is not
		// present or if the value cannot be casted to the asked return value.
		//
		template<typename Ret = Value>
		typename const_ref<Ret>::type get(std::string const& key) const
		{ return extract<Ret>::apply(_values.at(key)); }

		// Assign key to value.
		template<typename Ret = Value, typename T>
		typename const_ref<Ret>::type set(std::string const& key, T&& value)
		{
			auto it = _values.find(key);
			if (it == _values.end())
				return _set_new<Ret>(key, std::forward<T>(value));
			Value new_value = _convert<Ret>(std::forward<T>(value));
			if (!(it->second == new_value))
			{
				it->second = std::move(new_value);
				this->value_changed(it->first); // XXX not exception safe
			}
			return extract<Ret>::apply(it->second);
		}

		// Assign key to value not already set, returns associated value.
		template<typename Ret = Value, typename T>
		typename const_ref<Ret>::type set_default(std::string const& key,
		                                        T&& value)
		{
			auto it = _values.find(key);
			if (it == _values.end())
				return _set_new<Ret>(key, std::forward<T>(value));
			return extract<Ret>::apply(it->second);
		}

		void load(boost::filesystem::path const& path);
		void save(boost::filesystem::path const& path) const;

	public:
		template<typename Archive>
		void serialize(Archive& ar, unsigned int);
		template<class Archive>
		void save(Archive& ar, unsigned int) const;
		template<class Archive>
		void load(Archive& ar, unsigned int);

		std::vector<std::string> keys() const;

	protected:
		// Called when the value of an existing key has changed (default implem
		// does nothing).
		virtual void value_changed(std::string const& key);

		// Called when a key has been added (default implem does nothing).
		virtual void new_key(std::string const& key);

	public:
		// Upper case and replace dashes by underscores.
		static std::string normalize(std::string key);

	private:
		// Force insert.
		template<typename Ret, typename T>
		typename const_ref<Ret>::type _set_new(std::string const& key,
		                                       T&& value)
		{
			Value& ret = (_values[key] = _convert<Ret>(std::forward<T>(value)));
			this->new_key(key); // XXX not exception safe
			return extract<Ret>::apply(ret);
		}

		// Convert to a value
		template<typename Ret, typename T>
		Value _convert(T&& value) { return Value(Ret(std::forward<T>(value))); }

		template<typename Ret, typename T>
		Value _convert(std::vector<T> const& value)
		{
			std::vector<Value> res;
			res.reserve(value.size());
			for (auto&& el: value)
				res.emplace_back(el);
			return res;
		}

		template<typename Ret, typename T>
		Value _convert(std::vector<T>&& value)
		{
			std::vector<Value> res;
			res.reserve(value.size());
			for (auto&& el: value)
				res.emplace_back(std::move(el));
			return res;
		}

		template<typename Ret>
		Value _convert(std::vector<Value> const& value)
		{ return Value(value); }

		template<typename Ret>
		Value _convert(std::vector<Value>&& value)
		{ return Value(std::move(value)); }

		template<typename Ret>
		Value _convert(NodePtr const& node)
		{ return Value(Ret(_node_path(*node))); }

		template<typename Ret>
		Value _convert(Node const& node)
		{ return Value(Ret(_node_path(node))); }

		static
		boost::filesystem::path const& _node_path(Node const& node);
	};

	std::ostream& operator <<(std::ostream& out, Environ::Kind kind);

	// const_ref specialization -----------------------------------------------
	template<> struct Environ::const_ref<int>
	{ typedef int type; };

	template<> struct Environ::const_ref<bool>
	{ typedef bool type; };

	template<typename T> struct Environ::const_ref<std::vector<T>>
	{ typedef std::vector<T> type; };

	template<> struct Environ::const_ref<std::vector<Environ::Value>>
	{ typedef std::vector<Environ::Value> const& type; };

	// extract specialization -------------------------------------------------
	template<typename T> struct Environ::extract
	{
		typedef typename Environ::const_ref<T>::type return_type;
		static
		return_type apply(Value const& value)
		{ return boost::get<return_type>(value); }
	};

	template<> struct Environ::extract<Environ::Value>
	{
		static Environ::Value const& apply(Environ::Value const& value)
		{ return value; }
	};

	template<> struct Environ::extract<std::vector<Environ::Value>>
	{
		typedef std::vector<Environ::Value> const& return_type;
		static return_type apply(Environ::Value const& value)
		{ return boost::get<return_type>(value); }
	};

	template<typename T> struct Environ::extract<std::vector<T>>
	{
		typedef typename Environ::const_ref<std::vector<T>>::type return_type;
		typedef typename Environ::const_ref<T>::type value_type;
		static
		return_type apply(Value const& value)
		{
			auto const& vector = boost::get<std::vector<Value> const&>(value);
			return_type res;
			for (auto& el: vector)
				res.push_back(boost::get<value_type>(el));
			return res;
		}
	};

}
<commit_msg>Add missing virtual destructor.<commit_after>#pragma once

#include "fwd.hpp"

#include <boost/variant.hpp>
#include <boost/filesystem/path.hpp>

#include <iosfwd>
#include <memory>
#include <type_traits>
#include <vector>
#include <unordered_map>
#include <boost/functional/hash.hpp>

namespace configure {

	// Encapsulate a map where keys are strings and values are strings, path,
	// bool, or integers.
	//
	// The case of keys is ignored and characters '-' and '_' are considered
	// the same. Only ascii characters are accepted.
	class Environ
	{
	public:

	public:
		template<typename T> struct const_ref { typedef T const& type; };
		template<typename T> struct extract;
		enum class Kind {
			string,
			path,
			boolean,
			integer,
			list,
			none,
		};
		typedef boost::make_recursive_variant<
			std::string,
			boost::filesystem::path,
			bool,
			int,
			std::vector<boost::recursive_variant_>
		>::type Value;

	private:
		template<typename Char>
		static inline Char normalize_char(Char c)
		{
			static std::locale const locale;
			if (c == '-') return '_';
			return std::toupper(c, locale);
		}

		struct Hash
		{
			template<typename String>
			size_t operator ()(String const& value) const
			{
				size_t seed = 0;
				for (auto c: value)
					boost::hash_combine(seed, Environ::normalize_char(c));
				return seed;
			}
		};
		struct Equals
		{
			// My guess is that equals is called only when hashes matches
			// so the strings are likely to be equal.
			template<typename String>
			bool operator() (String const& lhs, String const& rhs) const
			{
				auto p1 = lhs.c_str();
				auto p2 = rhs.c_str();
				while (*p1 != '\0')
				{
					if (Environ::normalize_char(*p1) != Environ::normalize_char(*p2))
						return false;
					p1 += 1;
					p2 += 1;
				}
				return *p2 == '\0';
			}
		};
		typedef std::unordered_map<std::string, Value, Hash, Equals> Values;
		Values _values;

	public:
		Environ();
		Environ(Environ const& other);
		virtual ~Environ();

		bool has(std::string const& key) const;
		Kind kind(std::string const& key) const;
		std::string as_string(std::string const& key) const;

		// Returns an existing value of type T or throws if the key is not
		// present or if the value cannot be casted to the asked return value.
		//
		template<typename Ret = Value>
		typename const_ref<Ret>::type get(std::string const& key) const
		{ return extract<Ret>::apply(_values.at(key)); }

		// Assign key to value.
		template<typename Ret = Value, typename T>
		typename const_ref<Ret>::type set(std::string const& key, T&& value)
		{
			auto it = _values.find(key);
			if (it == _values.end())
				return _set_new<Ret>(key, std::forward<T>(value));
			Value new_value = _convert<Ret>(std::forward<T>(value));
			if (!(it->second == new_value))
			{
				it->second = std::move(new_value);
				this->value_changed(it->first); // XXX not exception safe
			}
			return extract<Ret>::apply(it->second);
		}

		// Assign key to value not already set, returns associated value.
		template<typename Ret = Value, typename T>
		typename const_ref<Ret>::type set_default(std::string const& key,
		                                        T&& value)
		{
			auto it = _values.find(key);
			if (it == _values.end())
				return _set_new<Ret>(key, std::forward<T>(value));
			return extract<Ret>::apply(it->second);
		}

		void load(boost::filesystem::path const& path);
		void save(boost::filesystem::path const& path) const;

	public:
		template<typename Archive>
		void serialize(Archive& ar, unsigned int);
		template<class Archive>
		void save(Archive& ar, unsigned int) const;
		template<class Archive>
		void load(Archive& ar, unsigned int);

		std::vector<std::string> keys() const;

	protected:
		// Called when the value of an existing key has changed (default implem
		// does nothing).
		virtual void value_changed(std::string const& key);

		// Called when a key has been added (default implem does nothing).
		virtual void new_key(std::string const& key);

	public:
		// Upper case and replace dashes by underscores.
		static std::string normalize(std::string key);

	private:
		// Force insert.
		template<typename Ret, typename T>
		typename const_ref<Ret>::type _set_new(std::string const& key,
		                                       T&& value)
		{
			Value& ret = (_values[key] = _convert<Ret>(std::forward<T>(value)));
			this->new_key(key); // XXX not exception safe
			return extract<Ret>::apply(ret);
		}

		// Convert to a value
		template<typename Ret, typename T>
		Value _convert(T&& value) { return Value(Ret(std::forward<T>(value))); }

		template<typename Ret, typename T>
		Value _convert(std::vector<T> const& value)
		{
			std::vector<Value> res;
			res.reserve(value.size());
			for (auto&& el: value)
				res.emplace_back(el);
			return res;
		}

		template<typename Ret, typename T>
		Value _convert(std::vector<T>&& value)
		{
			std::vector<Value> res;
			res.reserve(value.size());
			for (auto&& el: value)
				res.emplace_back(std::move(el));
			return res;
		}

		template<typename Ret>
		Value _convert(std::vector<Value> const& value)
		{ return Value(value); }

		template<typename Ret>
		Value _convert(std::vector<Value>&& value)
		{ return Value(std::move(value)); }

		template<typename Ret>
		Value _convert(NodePtr const& node)
		{ return Value(Ret(_node_path(*node))); }

		template<typename Ret>
		Value _convert(Node const& node)
		{ return Value(Ret(_node_path(node))); }

		static
		boost::filesystem::path const& _node_path(Node const& node);
	};

	std::ostream& operator <<(std::ostream& out, Environ::Kind kind);

	// const_ref specialization -----------------------------------------------
	template<> struct Environ::const_ref<int>
	{ typedef int type; };

	template<> struct Environ::const_ref<bool>
	{ typedef bool type; };

	template<typename T> struct Environ::const_ref<std::vector<T>>
	{ typedef std::vector<T> type; };

	template<> struct Environ::const_ref<std::vector<Environ::Value>>
	{ typedef std::vector<Environ::Value> const& type; };

	// extract specialization -------------------------------------------------
	template<typename T> struct Environ::extract
	{
		typedef typename Environ::const_ref<T>::type return_type;
		static
		return_type apply(Value const& value)
		{ return boost::get<return_type>(value); }
	};

	template<> struct Environ::extract<Environ::Value>
	{
		static Environ::Value const& apply(Environ::Value const& value)
		{ return value; }
	};

	template<> struct Environ::extract<std::vector<Environ::Value>>
	{
		typedef std::vector<Environ::Value> const& return_type;
		static return_type apply(Environ::Value const& value)
		{ return boost::get<return_type>(value); }
	};

	template<typename T> struct Environ::extract<std::vector<T>>
	{
		typedef typename Environ::const_ref<std::vector<T>>::type return_type;
		typedef typename Environ::const_ref<T>::type value_type;
		static
		return_type apply(Value const& value)
		{
			auto const& vector = boost::get<std::vector<Value> const&>(value);
			return_type res;
			for (auto& el: vector)
				res.push_back(boost::get<value_type>(el));
			return res;
		}
	};

}
<|endoftext|>


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

 The MIT License (MIT)

 Copyright (c) 2015 Dmitry Sovetov

 https://github.com/dmsovetov

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.

 **************************************************************************/

#include <Dreemchest.h>

DC_USE_DREEMCHEST

using namespace Platform;
using namespace Renderer;

static String s_vertexShader =
    "cbuffer Pass     pass     : 0;                     \n"
    "cbuffer Instance instance : 1;                     \n"
    "                                                   \n"
    "varying vec3 v_color;                              \n"
    "                                                   \n"
    "void main()                                        \n"
    "{                                                  \n"
    "   v_color     = gl_Normal * 0.5 + 0.5;            \n"
    "   gl_Position = pass.projection * pass.view * instance.transform * gl_Vertex; \n"
    "}                                                  \n"
;

static String s_fragmentShader =
    "varying vec3 v_color;                              \n"
    "                                                   \n"
    "void main()                                        \n"
    "{                                                  \n"
    "   gl_FragColor = vec4(v_color, 1.0);              \n"
    "}                                                  \n"
    ;

struct Pass
{
    Matrix4 projection;
    Matrix4 view;
    static UniformElement s_layout[];
} s_pass;

UniformElement Pass::s_layout[] =
{
      { "projection", UniformElement::Matrix4, offsetof(Pass, projection) }
    , { "view",       UniformElement::Matrix4, offsetof(Pass, view)       }
    , { NULL }
};

struct Instance
{
    Matrix4 transform;
    static UniformElement s_layout[];
} s_instance;

UniformElement Instance::s_layout[] =
{
      { "transform", UniformElement::Matrix4, offsetof(Instance, transform) }
    , { NULL }
};

class Textures : public RenderingApplicationDelegate
{
    StateBlock m_renderStates;
    RenderFrame m_renderFrame;
    MeshLoader::Descriptor mesh;
    ConstantBuffer_ m_instanceConstantBuffer;

    virtual void handleLaunched(Application* application) NIMBLE_OVERRIDE
    {
        Logger::setStandardLogger();

        if (!initialize(800 / 4, 600 / 4))
        {
            application->quit(-1);
        }

        // First load a mesh from a file
        mesh = MeshLoader::objFromFile("Assets/Meshes/column.obj");

        if (!mesh)
        {
            application->quit(-1);
        }

        // Now configure a mesh rendering states
        InputLayout inputLayout = m_renderingContext->requestInputLayout(mesh.format);
        VertexBuffer_ vertexBuffer = m_renderingContext->requestVertexBuffer(&mesh.vertices[0], sizeof(MeshLoader::Vertex) * mesh.vertices.size());

        m_renderStates.bindInputLayout(inputLayout);
        m_renderStates.bindVertexBuffer(vertexBuffer);

        if (mesh.indices.size())
        {
            IndexBuffer_ indexBuffer = m_renderingContext->requestIndexBuffer(&mesh.indices[0], sizeof(u16) * mesh.indices.size());
            m_renderStates.bindIndexBuffer(indexBuffer);
        }

        // Configure pass constant buffer
        {
            s_pass.projection = Matrix4::perspective(60.0f, m_window->aspectRatio(), 0.1f, 100.0f);
            s_pass.view       = Matrix4::lookAt(Vec3(0.0f, 5.0f, -5.0f), Vec3(0.0f, 0.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f));
            UniformLayout uniformLayout = m_renderingContext->requestUniformLayout("Pass", Pass::s_layout);
            ConstantBuffer_ constantBuffer = m_renderingContext->requestConstantBuffer(&s_pass, sizeof(s_pass), uniformLayout);
            m_renderStates.bindConstantBuffer(constantBuffer, 0);
        }

        // Configure instance constant buffer
        {
            UniformLayout uniformLayout = m_renderingContext->requestUniformLayout("Instance", Instance::s_layout);
            m_instanceConstantBuffer = m_renderingContext->requestConstantBuffer(&s_instance, sizeof(s_instance), uniformLayout);
            m_renderStates.bindConstantBuffer(m_instanceConstantBuffer, 1);
        }

        // Create a simple shader program
        Program program = m_renderingContext->requestProgram(s_vertexShader, s_fragmentShader);
        m_renderStates.bindProgram(program);
    }

    virtual void handleRenderFrame(const Window::Update& e) NIMBLE_OVERRIDE
    {
        m_renderFrame.clear();

        CommandBuffer& commands = m_renderFrame.entryPoint();

        // Clear the viewport
        commands.clear(Rgba(0.3f, 0.3f, 0.3f), ClearAll);

        // Update an instance constant buffer
        s_instance.transform = Matrix4::rotateXY(0.0f, currentTime() * 0.001f);
        commands.uploadConstantBuffer(m_instanceConstantBuffer, &s_instance, sizeof(s_instance));

        // Render the mesh
        commands.drawPrimitives(0, Renderer::PrimTriangles, 0, mesh.vertices.size(), &m_renderStates);

        m_renderingContext->display(m_renderFrame);
    }
};

dcDeclareApplication(new Textures)
<commit_msg>Fixed: camera setting in Meshes rendering example<commit_after>/**************************************************************************

 The MIT License (MIT)

 Copyright (c) 2015 Dmitry Sovetov

 https://github.com/dmsovetov

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.

 **************************************************************************/

#include <Dreemchest.h>

DC_USE_DREEMCHEST

using namespace Platform;
using namespace Renderer;

static String s_vertexShader =
    "cbuffer Pass     pass     : 0;                     \n"
    "cbuffer Instance instance : 1;                     \n"
    "                                                   \n"
    "varying vec3 v_color;                              \n"
    "                                                   \n"
    "void main()                                        \n"
    "{                                                  \n"
    "   v_color     = gl_Normal * 0.5 + 0.5;            \n"
    "   gl_Position = pass.projection * pass.view * instance.transform * gl_Vertex; \n"
    "}                                                  \n"
;

static String s_fragmentShader =
    "varying vec3 v_color;                              \n"
    "                                                   \n"
    "void main()                                        \n"
    "{                                                  \n"
    "   gl_FragColor = vec4(v_color, 1.0);              \n"
    "}                                                  \n"
    ;

struct Pass
{
    Matrix4 projection;
    Matrix4 view;
    static UniformElement s_layout[];
} s_pass;

UniformElement Pass::s_layout[] =
{
      { "projection", UniformElement::Matrix4, offsetof(Pass, projection) }
    , { "view",       UniformElement::Matrix4, offsetof(Pass, view)       }
    , { NULL }
};

struct Instance
{
    Matrix4 transform;
    static UniformElement s_layout[];
} s_instance;

UniformElement Instance::s_layout[] =
{
      { "transform", UniformElement::Matrix4, offsetof(Instance, transform) }
    , { NULL }
};

class Textures : public RenderingApplicationDelegate
{
    StateBlock m_renderStates;
    RenderFrame m_renderFrame;
    MeshLoader::Descriptor mesh;
    ConstantBuffer_ m_instanceConstantBuffer;

    virtual void handleLaunched(Application* application) NIMBLE_OVERRIDE
    {
        Logger::setStandardLogger();

        if (!initialize(800 / 4, 600 / 4))
        {
            application->quit(-1);
        }

        // First load a mesh from a file
        mesh = MeshLoader::objFromFile("Assets/Meshes/column.obj");

        if (!mesh)
        {
            application->quit(-1);
        }

        // Now configure a mesh rendering states
        InputLayout inputLayout = m_renderingContext->requestInputLayout(mesh.format);
        VertexBuffer_ vertexBuffer = m_renderingContext->requestVertexBuffer(&mesh.vertices[0], sizeof(MeshLoader::Vertex) * mesh.vertices.size());

        m_renderStates.bindInputLayout(inputLayout);
        m_renderStates.bindVertexBuffer(vertexBuffer);

        if (mesh.indices.size())
        {
            IndexBuffer_ indexBuffer = m_renderingContext->requestIndexBuffer(&mesh.indices[0], sizeof(u16) * mesh.indices.size());
            m_renderStates.bindIndexBuffer(indexBuffer);
        }

        // Configure pass constant buffer
        {
            s_pass.projection = Matrix4::perspective(60.0f, m_window->aspectRatio(), 0.1f, 100.0f);
            s_pass.view       = Matrix4::lookAt(Vec3(0.0f, 15.0f, -15.0f), Vec3(0.0f, 7.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f));
            UniformLayout uniformLayout = m_renderingContext->requestUniformLayout("Pass", Pass::s_layout);
            ConstantBuffer_ constantBuffer = m_renderingContext->requestConstantBuffer(&s_pass, sizeof(s_pass), uniformLayout);
            m_renderStates.bindConstantBuffer(constantBuffer, 0);
        }

        // Configure instance constant buffer
        {
            UniformLayout uniformLayout = m_renderingContext->requestUniformLayout("Instance", Instance::s_layout);
            m_instanceConstantBuffer = m_renderingContext->requestConstantBuffer(&s_instance, sizeof(s_instance), uniformLayout);
            m_renderStates.bindConstantBuffer(m_instanceConstantBuffer, 1);
        }

        // Create a simple shader program
        Program program = m_renderingContext->requestProgram(s_vertexShader, s_fragmentShader);
        m_renderStates.bindProgram(program);
    }

    virtual void handleRenderFrame(const Window::Update& e) NIMBLE_OVERRIDE
    {
        m_renderFrame.clear();

        CommandBuffer& commands = m_renderFrame.entryPoint();

        // Clear the viewport
        commands.clear(Rgba(0.3f, 0.3f, 0.3f), ClearAll);

        // Update an instance constant buffer
        s_instance.transform = Matrix4::rotateXY(0.0f, currentTime() * 0.001f);
        commands.uploadConstantBuffer(m_instanceConstantBuffer, &s_instance, sizeof(s_instance));

        // Render the mesh
        commands.drawPrimitives(0, Renderer::PrimTriangles, 0, mesh.vertices.size(), &m_renderStates);

        m_renderingContext->display(m_renderFrame);
    }
};

dcDeclareApplication(new Textures)
<|endoftext|>


<commit_before>// MediaInfo_Internal - All info about media files
// Copyright (C) 2002-2010 MediaArea.net SARL, Info@MediaArea.net
//
// 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 3 of the License, or
// 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/>.
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
// For user: you can disable or enable it
//#define MEDIAINFO_DEBUG
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Compilation conditions
#include "MediaInfo/Setup.h"
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "MediaInfo/Reader/Reader_File.h"
#include "MediaInfo/File__Analyze.h"
#include "ZenLib/FileName.h"
using namespace ZenLib;
using namespace std;
//---------------------------------------------------------------------------
// Debug stuff
#ifdef MEDIAINFO_DEBUG
    int64u Reader_File_Offset=0;
    int64u Reader_File_BytesRead_Total=0;
    int64u Reader_File_BytesRead=0;
    int64u Reader_File_Count=1;
    #include <iostream>
#endif // MEDIAINFO_DEBUG
//---------------------------------------------------------------------------

namespace MediaInfoLib
{

const size_t Buffer_NormalSize=/*188*7;//*/64*1024;
const size_t Buffer_NoJump=128*1024;

//---------------------------------------------------------------------------
size_t Reader_File::Format_Test(MediaInfo_Internal* MI, const String &File_Name)
{
    #if MEDIAINFO_EVENTS
        {
            struct MediaInfo_Event_General_Start_0 Event;
            Event.EventCode=MediaInfo_EventCode_Create(MediaInfo_Parser_None, MediaInfo_Event_General_Start, 0);
            Event.Stream_Size=File::Size_Get(File_Name);
            MI->Config.Event_Send((const int8u*)&Event, sizeof(MediaInfo_Event_General_Start_0));
        }
    #endif //MEDIAINFO_EVENTS

    //With Parser MultipleParsing
    /*
    MI->Open_Buffer_Init((int64u)-1, File_Name);
    if (Format_Test_PerParser(MI, File_Name))
         return 1;
    return 0; //There is a problem
    */

    //Get the Extension
    Ztring Extension=FileName::Extension_Get(File_Name);
    Extension.MakeLowerCase();

    //Search the theorical format from extension
    InfoMap &FormatList=MediaInfoLib::Config.Format_Get();
    InfoMap::iterator Format=FormatList.begin();
    while (Format!=FormatList.end())
    {
        const Ztring &Extensions=FormatList.Get(Format->first, InfoFormat_Extensions);
        if (Extensions.find(Extension)!=Error)
        {
            if(Extension.size()==Extensions.size())
                break; //Only one extenion in the list
            if(Extensions.find(Extension+_T(" "))!=Error
            || Extensions.find(_T(" ")+Extension)!=Error)
                break;
        }
        Format++;
    }
    if (Format!=FormatList.end())
    {
        const Ztring &Parser=Format->second(InfoFormat_Parser);
        if (MI->SelectFromExtension(Parser))
        {
            //Test the theorical format
            if (Format_Test_PerParser(MI, File_Name)>0)
                 return 1;
        }
    }

    size_t ToReturn=MI->ListFormats(File_Name);
    return ToReturn;
}

//---------------------------------------------------------------------------
size_t Reader_File::Format_Test_PerParser(MediaInfo_Internal* MI, const String &File_Name)
{
    //Opening the file
    F.Open(File_Name);
    if (!F.Opened_Get())
        return 0;

    //Buffer
    Buffer_Size_Max=Buffer_NormalSize;
    Buffer=new int8u[Buffer_Size_Max];

    //Partial file handling
    Ztring Config_Partial_Begin=MI->Config.File_Partial_Begin_Get();
    if (!Config_Partial_Begin.empty() && Config_Partial_Begin[0]>=_T('0') && Config_Partial_Begin[0]<=_T('9'))
    {
        if (Config_Partial_Begin.find(_T('%'))==Config_Partial_Begin.size()-1)
            Partial_Begin=float64_int64s(F.Size_Get()*Config_Partial_Begin.To_float64()/100);
        else
            Partial_Begin=Config_Partial_Begin.To_int64u();
    }
    else
        Partial_Begin=0;
    Ztring Config_Partial_End=MI->Config.File_Partial_End_Get();
    if (!Config_Partial_End.empty() && Config_Partial_End[0]>=_T('0') && Config_Partial_End[0]<=_T('9'))
    {
        if (Config_Partial_End.find(_T('%'))==Config_Partial_End.size()-1)
            Partial_End=float64_int64s(F.Size_Get()*Config_Partial_End.To_float64()/100);
        else
            Partial_End=Config_Partial_End.To_int64u();
    }
    else
        Partial_End=F.Size_Get();
    if (Partial_Begin>F.Size_Get())
        Partial_Begin=0; //Wrong value
    if (Partial_End>F.Size_Get())
        Partial_End=F.Size_Get(); //Wrong value
    if (Partial_Begin>Partial_End)
    {
        Partial_Begin=0; //Wrong value
        Partial_End=F.Size_Get(); //Wrong value
    }

    //Parser
    MI->Open_Buffer_Init(Partial_End-Partial_Begin, File_Name);

    //Test the format with buffer
    return Format_Test_PerParser_Continue(MI);
}

//---------------------------------------------------------------------------
size_t Reader_File::Format_Test_PerParser_Continue (MediaInfo_Internal* MI)
{
    bool StopAfterFilled=MI->Config.File_StopAfterFilled_Get();

    #if MEDIAINFO_DEMUX
    //PerPacket
    if (MI->Config.Demux_EventWasSent)
    {
        MI->Config.Demux_EventWasSent=false;

        //Parser
        Status=MI->Open_Buffer_Continue(NULL, 0);

        //Demux
        if (MI->Config.Demux_EventWasSent)
            return 2; //Must return immediately

        //Threading
        if (MI->IsTerminating())
            return 1; //Termination is requested

        if (!(!(Status[File__Analyze::IsFinished] || (StopAfterFilled && Status[File__Analyze::IsFilled]))))
            return 1;
    }
    #endif //MEDIAINFO_DEMUX

    //Test the format with buffer
    do
    {
        //Seek (if needed)
        if (MI->Open_Buffer_Continue_GoTo_Get()!=(int64u)-1)
        {
            #ifdef MEDIAINFO_DEBUG
                std::cout<<std::hex<<Reader_File_Offset<<" - "<<Reader_File_Offset+Reader_File_BytesRead<<" : "<<std::dec<<Reader_File_BytesRead<<" bytes"<<std::endl;
                Reader_File_Offset=MI->Open_Buffer_Continue_GoTo_Get();
                Reader_File_BytesRead=0;
                Reader_File_Count++;
            #endif //MEDIAINFO_DEBUG

            if (MI->Open_Buffer_Continue_GoTo_Get()>=F.Size_Get())
                break; //Seek requested, but on a file bigger in theory than what is in the real file, we can't do this
            if (!(MI->Open_Buffer_Continue_GoTo_Get()>F.Position_Get() && MI->Open_Buffer_Continue_GoTo_Get()<F.Position_Get()+Buffer_NoJump)) //No smal jumps
            {
                 if (!F.GoTo(Partial_Begin+MI->Open_Buffer_Continue_GoTo_Get()))
                    break; //File is not seekable

                MI->Open_Buffer_Init((int64u)-1, F.Position_Get());
            }
        }

        //Buffering
        size_t Buffer_Size=F.Read(Buffer, (F.Position_Get()+Buffer_Size_Max<Partial_End)?Buffer_Size_Max:((size_t)(Partial_End-F.Position_Get())));
        if (Buffer_Size==0)
            break; //Problem while reading

        #ifdef MEDIAINFO_DEBUG
            Reader_File_BytesRead_Total+=Buffer_Size;
            Reader_File_BytesRead+=Buffer_Size;
        #endif //MEDIAINFO_DEBUG

        //Parser
        Status=MI->Open_Buffer_Continue(Buffer, Buffer_Size);

        #if MEDIAINFO_DEMUX
            if (MI->Config.Demux_EventWasSent)
                return 2; //Must return immediately
        #endif //MEDIAINFO_DEMUX

        //Threading
        if (MI->IsTerminating())
            break; //Termination is requested
    }
    while (!(Status[File__Analyze::IsFinished] || (StopAfterFilled && Status[File__Analyze::IsFilled])));
    if (F.Size_Get()==0) //If Size==0, Status is never updated
        Status=MI->Open_Buffer_Continue(NULL, 0);

    #ifdef MEDIAINFO_DEBUG
        std::cout<<std::hex<<Reader_File_Offset<<" - "<<Reader_File_Offset+Reader_File_BytesRead<<" : "<<std::dec<<Reader_File_BytesRead<<" bytes"<<std::endl;
        std::cout<<"Total: "<<std::dec<<Reader_File_BytesRead_Total<<" bytes in "<<Reader_File_Count<<" blocks"<<std::endl;
    #endif //MEDIAINFO_DEBUG

    //File
    F.Close();

    //Buffer
    delete[] Buffer; //Buffer=NULL;

    //Is this file detected?
    if (!Status[File__Analyze::IsAccepted])
        return 0;

    MI->Open_Buffer_Finalize();

    return 1;
}

} //NameSpace

<commit_msg>+ File interface: possibility to indicate begin and end offset of the analysis (partial analysis) (patch)<commit_after>// MediaInfo_Internal - All info about media files
// Copyright (C) 2002-2010 MediaArea.net SARL, Info@MediaArea.net
//
// 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 3 of the License, or
// 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/>.
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------
// For user: you can disable or enable it
//#define MEDIAINFO_DEBUG
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Compilation conditions
#include "MediaInfo/Setup.h"
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "MediaInfo/Reader/Reader_File.h"
#include "MediaInfo/File__Analyze.h"
#include "ZenLib/FileName.h"
using namespace ZenLib;
using namespace std;
//---------------------------------------------------------------------------
// Debug stuff
#ifdef MEDIAINFO_DEBUG
    int64u Reader_File_Offset=0;
    int64u Reader_File_BytesRead_Total=0;
    int64u Reader_File_BytesRead=0;
    int64u Reader_File_Count=1;
    #include <iostream>
#endif // MEDIAINFO_DEBUG
//---------------------------------------------------------------------------

namespace MediaInfoLib
{

const size_t Buffer_NormalSize=/*188*7;//*/64*1024;
const size_t Buffer_NoJump=128*1024;

//---------------------------------------------------------------------------
size_t Reader_File::Format_Test(MediaInfo_Internal* MI, const String &File_Name)
{
    #if MEDIAINFO_EVENTS
        {
            struct MediaInfo_Event_General_Start_0 Event;
            Event.EventCode=MediaInfo_EventCode_Create(MediaInfo_Parser_None, MediaInfo_Event_General_Start, 0);
            Event.Stream_Size=File::Size_Get(File_Name);
            MI->Config.Event_Send((const int8u*)&Event, sizeof(MediaInfo_Event_General_Start_0));
        }
    #endif //MEDIAINFO_EVENTS

    //With Parser MultipleParsing
    /*
    MI->Open_Buffer_Init((int64u)-1, File_Name);
    if (Format_Test_PerParser(MI, File_Name))
         return 1;
    return 0; //There is a problem
    */

    //Get the Extension
    Ztring Extension=FileName::Extension_Get(File_Name);
    Extension.MakeLowerCase();

    //Search the theorical format from extension
    InfoMap &FormatList=MediaInfoLib::Config.Format_Get();
    InfoMap::iterator Format=FormatList.begin();
    while (Format!=FormatList.end())
    {
        const Ztring &Extensions=FormatList.Get(Format->first, InfoFormat_Extensions);
        if (Extensions.find(Extension)!=Error)
        {
            if(Extension.size()==Extensions.size())
                break; //Only one extenion in the list
            if(Extensions.find(Extension+_T(" "))!=Error
            || Extensions.find(_T(" ")+Extension)!=Error)
                break;
        }
        Format++;
    }
    if (Format!=FormatList.end())
    {
        const Ztring &Parser=Format->second(InfoFormat_Parser);
        if (MI->SelectFromExtension(Parser))
        {
            //Test the theorical format
            if (Format_Test_PerParser(MI, File_Name)>0)
                 return 1;
        }
    }

    size_t ToReturn=MI->ListFormats(File_Name);
    return ToReturn;
}

//---------------------------------------------------------------------------
size_t Reader_File::Format_Test_PerParser(MediaInfo_Internal* MI, const String &File_Name)
{
    //Opening the file
    F.Open(File_Name);
    if (!F.Opened_Get())
        return 0;

    //Buffer
    Buffer_Size_Max=Buffer_NormalSize;
    Buffer=new int8u[Buffer_Size_Max];

    //Partial file handling
    Ztring Config_Partial_Begin=MI->Config.File_Partial_Begin_Get();
    if (!Config_Partial_Begin.empty() && Config_Partial_Begin[0]>=_T('0') && Config_Partial_Begin[0]<=_T('9'))
    {
        if (Config_Partial_Begin.find(_T('%'))==Config_Partial_Begin.size()-1)
            Partial_Begin=float64_int64s(F.Size_Get()*Config_Partial_Begin.To_float64()/100);
        else
            Partial_Begin=Config_Partial_Begin.To_int64u();
        if (Partial_Begin)
            F.GoTo(Partial_Begin);
    }
    else
        Partial_Begin=0;
    Ztring Config_Partial_End=MI->Config.File_Partial_End_Get();
    if (!Config_Partial_End.empty() && Config_Partial_End[0]>=_T('0') && Config_Partial_End[0]<=_T('9'))
    {
        if (Config_Partial_End.find(_T('%'))==Config_Partial_End.size()-1)
            Partial_End=float64_int64s(F.Size_Get()*Config_Partial_End.To_float64()/100);
        else
            Partial_End=Config_Partial_End.To_int64u();
    }
    else
        Partial_End=F.Size_Get();
    if (Partial_Begin>F.Size_Get())
        Partial_Begin=0; //Wrong value
    if (Partial_End>F.Size_Get())
        Partial_End=F.Size_Get(); //Wrong value
    if (Partial_Begin>Partial_End)
    {
        Partial_Begin=0; //Wrong value
        Partial_End=F.Size_Get(); //Wrong value
    }

    //Parser
    MI->Open_Buffer_Init(Partial_End-Partial_Begin, File_Name);

    //Test the format with buffer
    return Format_Test_PerParser_Continue(MI);
}

//---------------------------------------------------------------------------
size_t Reader_File::Format_Test_PerParser_Continue (MediaInfo_Internal* MI)
{
    bool StopAfterFilled=MI->Config.File_StopAfterFilled_Get();

    #if MEDIAINFO_DEMUX
    //PerPacket
    if (MI->Config.Demux_EventWasSent)
    {
        MI->Config.Demux_EventWasSent=false;

        //Parser
        Status=MI->Open_Buffer_Continue(NULL, 0);

        //Demux
        if (MI->Config.Demux_EventWasSent)
            return 2; //Must return immediately

        //Threading
        if (MI->IsTerminating())
            return 1; //Termination is requested

        if (!(!(Status[File__Analyze::IsFinished] || (StopAfterFilled && Status[File__Analyze::IsFilled]))))
            return 1;
    }
    #endif //MEDIAINFO_DEMUX

    //Test the format with buffer
    do
    {
        //Seek (if needed)
        if (MI->Open_Buffer_Continue_GoTo_Get()!=(int64u)-1)
        {
            #ifdef MEDIAINFO_DEBUG
                std::cout<<std::hex<<Reader_File_Offset<<" - "<<Reader_File_Offset+Reader_File_BytesRead<<" : "<<std::dec<<Reader_File_BytesRead<<" bytes"<<std::endl;
                Reader_File_Offset=MI->Open_Buffer_Continue_GoTo_Get();
                Reader_File_BytesRead=0;
                Reader_File_Count++;
            #endif //MEDIAINFO_DEBUG

            if (MI->Open_Buffer_Continue_GoTo_Get()>=F.Size_Get())
                break; //Seek requested, but on a file bigger in theory than what is in the real file, we can't do this
            if (!(MI->Open_Buffer_Continue_GoTo_Get()>F.Position_Get() && MI->Open_Buffer_Continue_GoTo_Get()<F.Position_Get()+Buffer_NoJump)) //No smal jumps
            {
                 if (!F.GoTo(Partial_Begin+MI->Open_Buffer_Continue_GoTo_Get()))
                    break; //File is not seekable

                MI->Open_Buffer_Init((int64u)-1, F.Position_Get()-Partial_Begin);
            }
        }

        //Buffering
        size_t Buffer_Size=F.Read(Buffer, (F.Position_Get()+Buffer_Size_Max<Partial_End)?Buffer_Size_Max:((size_t)(Partial_End-F.Position_Get())));
        if (Buffer_Size==0)
            break; //Problem while reading

        #ifdef MEDIAINFO_DEBUG
            Reader_File_BytesRead_Total+=Buffer_Size;
            Reader_File_BytesRead+=Buffer_Size;
        #endif //MEDIAINFO_DEBUG

        //Parser
        Status=MI->Open_Buffer_Continue(Buffer, Buffer_Size);

        #if MEDIAINFO_DEMUX
            if (MI->Config.Demux_EventWasSent)
                return 2; //Must return immediately
        #endif //MEDIAINFO_DEMUX

        //Threading
        if (MI->IsTerminating())
            break; //Termination is requested
    }
    while (!(Status[File__Analyze::IsFinished] || (StopAfterFilled && Status[File__Analyze::IsFilled])));
    if (F.Size_Get()==0) //If Size==0, Status is never updated
        Status=MI->Open_Buffer_Continue(NULL, 0);

    #ifdef MEDIAINFO_DEBUG
        std::cout<<std::hex<<Reader_File_Offset<<" - "<<Reader_File_Offset+Reader_File_BytesRead<<" : "<<std::dec<<Reader_File_BytesRead<<" bytes"<<std::endl;
        std::cout<<"Total: "<<std::dec<<Reader_File_BytesRead_Total<<" bytes in "<<Reader_File_Count<<" blocks"<<std::endl;
    #endif //MEDIAINFO_DEBUG

    //File
    F.Close();

    //Buffer
    delete[] Buffer; //Buffer=NULL;

    //Is this file detected?
    if (!Status[File__Analyze::IsAccepted])
        return 0;

    MI->Open_Buffer_Finalize();

    return 1;
}

} //NameSpace

<|endoftext|>