<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.
 *
 *=========================================================================*/
#ifndef itkReinitializeLevelSetImageFilter_hxx
#define itkReinitializeLevelSetImageFilter_hxx

#include "itkReinitializeLevelSetImageFilter.h"
#include "itkImageRegionIterator.h"
#include "itkIndex.h"

namespace itk
{
/**
 * Default constructor.
 */
template< typename TLevelSet >
ReinitializeLevelSetImageFilter< TLevelSet >
::ReinitializeLevelSetImageFilter()
{
  m_LevelSetValue = 0.0;

  m_Locator = LocatorType::New();
  m_Marcher = FastMarchingImageFilterType::New();

  m_NarrowBanding = false;
  m_InputNarrowBandwidth = 12.0;
  m_OutputNarrowBandwidth = 12.0;
  m_InputNarrowBand = ITK_NULLPTR;
  m_OutputNarrowBand = ITK_NULLPTR;
}

/*
 * Set the input narrowband container.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::SetInputNarrowBand(
  NodeContainer *ptr)
{
  if ( m_InputNarrowBand != ptr )
    {
    m_InputNarrowBand = ptr;
    this->Modified();
    }
}

/**
 * PrintSelf method.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);
  os << indent << "Level set value: " << m_LevelSetValue << std::endl;
  os << indent << "Narrowbanding: " << m_NarrowBanding << std::endl;
  os << indent << "Input narrow bandwidth: " << m_InputNarrowBandwidth;
  os << std::endl;
  os << indent << "Output narrow bandwidth: " << m_OutputNarrowBandwidth;
  os << std::endl;
  os << indent << "Input narrow band: " << m_InputNarrowBand.GetPointer();
  os << std::endl;
  os << indent << "Output narrow band: " << m_OutputNarrowBand.GetPointer();
  os << std::endl;
}

/*
 * GenerateInputRequestedRegion method.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::GenerateInputRequestedRegion()
{
  // use the default implementation.
  this->Superclass::GenerateInputRequestedRegion();
}

/*
 * EnlargeOutputRequestedRegion method.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::EnlargeOutputRequestedRegion(
  DataObject *output)
{
  // this filter requires the all of the output image to be in
  // the buffer
  TLevelSet *imgData;

  imgData = dynamic_cast< TLevelSet * >( output );
  if ( imgData )
    {
    imgData->SetRequestedRegionToLargestPossibleRegion();
    }
  else
    {
    // pointer could not be cast to TLevelSet *
    itkWarningMacro( << "itk::ReinitializeLevelSetImageFilter"
                     << "::EnlargeOutputRequestedRegion cannot cast "
                     << typeid( output ).name() << " to "
                     << typeid( TLevelSet * ).name() );
    }
}

/*
 * Allocate/initialize memory.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::AllocateOutput()
{
  LevelSetPointer outputPtr = this->GetOutput();

  // allocate the output buffer memory
  outputPtr->SetBufferedRegion(
    outputPtr->GetRequestedRegion() );
  outputPtr->Allocate();

  // set the marcher output size
  m_Marcher->SetOutputSize(
    outputPtr->GetRequestedRegion().GetSize() );
  this->m_Marcher->SetOutputOrigin( this->GetInput()->GetOrigin() );
  this->m_Marcher->SetOutputSpacing( this->GetInput()->GetSpacing() );
  this->m_Marcher->SetOutputDirection( this->GetInput()->GetDirection() );
}

/*
 * Generate the output data.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::GenerateData()
{
  this->AllocateOutput();

  if ( m_NarrowBanding )
    {
    this->GenerateDataNarrowBand();
    }
  else
    {
    this->GenerateDataFull();
    }
}

/*
 * Generate the output data - full set version.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::GenerateDataFull()
{
  LevelSetConstPointer inputPtr = this->GetInput();
  LevelSetPointer      outputPtr = this->GetOutput();
  LevelSetPointer      tempLevelSet = m_Marcher->GetOutput();

  // define iterators
  typedef ImageRegionIterator< LevelSetImageType >      IteratorType;
  typedef ImageRegionConstIterator< LevelSetImageType > ConstIteratorType;

  ConstIteratorType inputIt( inputPtr,
                             inputPtr->GetBufferedRegion() );
  IteratorType outputIt( outputPtr,
                         outputPtr->GetBufferedRegion() );

  IteratorType tempIt;

  this->UpdateProgress(0.0);

  // locate the level set
  m_Locator->SetInputLevelSet(inputPtr);
  m_Locator->SetLevelSetValue(m_LevelSetValue);
  m_Locator->Locate();

  this->UpdateProgress(0.33);

  // march outward
  m_Marcher->SetTrialPoints( m_Locator->GetOutsidePoints() );
  m_Marcher->Update();

  tempIt = IteratorType( tempLevelSet,
                         tempLevelSet->GetBufferedRegion() );

  double value;

  while ( !inputIt.IsAtEnd() )
    {
    value = (double)inputIt.Get();
    if ( value - m_LevelSetValue > 0 )
      {
      outputIt.Set( tempIt.Get() );
      }

    ++inputIt;
    ++outputIt;
    ++tempIt;
    }

  this->UpdateProgress(0.66);

  // march inward
  m_Marcher->SetTrialPoints( m_Locator->GetInsidePoints() );
  m_Marcher->Update();

  inputIt.GoToBegin();
  outputIt.GoToBegin();
  tempIt.GoToBegin();

  while ( !inputIt.IsAtEnd() )
    {
    value = (double)inputIt.Get();
    if ( value - m_LevelSetValue <= 0 )
      {
      value = (double)tempIt.Get();
      outputIt.Set(-1.0 * value);
      }

    ++inputIt;
    ++outputIt;
    ++tempIt;
    }
}

/*
 * Generate output data - narrowband version.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::GenerateDataNarrowBand()
{
  LevelSetConstPointer inputPtr = this->GetInput();
  LevelSetPointer      outputPtr = this->GetOutput();
  LevelSetPointer      tempLevelSet = m_Marcher->GetOutput();

  // define iterators
  typedef ImageRegionIterator< LevelSetImageType >      IteratorType;
  typedef ImageRegionConstIterator< LevelSetImageType > ConstIteratorType;

  ConstIteratorType inputIt( inputPtr,
                             inputPtr->GetBufferedRegion() );

  IteratorType outputIt( outputPtr,
                         outputPtr->GetBufferedRegion() );

  PixelType posInfinity;
  PixelType negInfinity;

  posInfinity = NumericTraits< PixelType >::max();
  negInfinity = NumericTraits< PixelType >::NonpositiveMin();

  // set all internal pixels to minus infinity and
  // all external pixels to positive infinity
  double value;

  while ( !inputIt.IsAtEnd() )
    {
    value = (double)inputIt.Get();
    if ( value - m_LevelSetValue <= 0 )
      {
      outputIt.Set(negInfinity);
      }
    else
      {
      outputIt.Set(posInfinity);
      }

    ++inputIt;
    ++outputIt;
    }

  // create a new output narrowband container
  m_OutputNarrowBand = NodeContainer::New();

  this->UpdateProgress(0.0);

  // locate the level set
  m_Locator->SetInputLevelSet(inputPtr);
  m_Locator->SetLevelSetValue(m_LevelSetValue);

  if ( m_NarrowBanding && m_InputNarrowBand )
    {
    m_Locator->NarrowBandingOn();
    m_Locator->SetNarrowBandwidth(m_InputNarrowBandwidth);
    m_Locator->SetInputNarrowBand(m_InputNarrowBand);
    }
  else
    {
    m_Locator->NarrowBandingOff();
    }

  m_Locator->Locate();

  this->UpdateProgress(0.33);

  // march outward
  double stoppingValue = ( m_OutputNarrowBandwidth / 2.0 ) + 2.0;
  m_Marcher->SetStoppingValue(stoppingValue);
  m_Marcher->CollectPointsOn();
  m_Marcher->SetTrialPoints( m_Locator->GetOutsidePoints() );
  m_Marcher->Update();

  NodeContainerPointer procPoints = m_Marcher->GetProcessedPoints();

  typename NodeContainer::ConstIterator pointsIt;
  typename NodeContainer::ConstIterator pointsEnd;

  pointsIt = procPoints->Begin();
  pointsEnd = procPoints->End();

  NodeType  node;
  PixelType inPixel;

  for (; pointsIt != pointsEnd; ++pointsIt )
    {
    node = pointsIt.Value();
    inPixel = inputPtr->GetPixel( node.GetIndex() );

    value = (double)inPixel;
    if ( value - m_LevelSetValue > 0 )
      {
      inPixel = tempLevelSet->GetPixel( node.GetIndex() );
      outputPtr->SetPixel(node.GetIndex(), inPixel);
      m_OutputNarrowBand->InsertElement(m_OutputNarrowBand->Size(), node);
      }
    } // end for loop

  this->UpdateProgress(0.66);

  // march inward
  m_Marcher->SetTrialPoints( m_Locator->GetInsidePoints() );
  m_Marcher->Update();

  procPoints = m_Marcher->GetProcessedPoints();
  pointsIt = procPoints->Begin();
  pointsEnd = procPoints->End();

  for (; pointsIt != pointsEnd; ++pointsIt )
    {
    node = pointsIt.Value();
    inPixel = inputPtr->GetPixel( node.GetIndex() );

    value = (double)inPixel;
    if ( value - m_LevelSetValue <= 0 )
      {
      inPixel = tempLevelSet->GetPixel( node.GetIndex() );
      value = (double)inPixel;
      inPixel =  -1.0 * value;
      outputPtr->SetPixel(node.GetIndex(), inPixel);
      node.SetValue(node.GetValue() * -1.0);
      m_OutputNarrowBand->InsertElement(m_OutputNarrowBand->Size(), node);
      }
    } // end for loop
}
} // namespace itk

#endif
<commit_msg>BUG: Do not assume zero Index in ReinitializeLevelSetImageFilter<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.
 *
 *=========================================================================*/
#ifndef itkReinitializeLevelSetImageFilter_hxx
#define itkReinitializeLevelSetImageFilter_hxx

#include "itkReinitializeLevelSetImageFilter.h"
#include "itkImageRegionIterator.h"
#include "itkIndex.h"

namespace itk
{
/**
 * Default constructor.
 */
template< typename TLevelSet >
ReinitializeLevelSetImageFilter< TLevelSet >
::ReinitializeLevelSetImageFilter()
{
  m_LevelSetValue = 0.0;

  m_Locator = LocatorType::New();
  m_Marcher = FastMarchingImageFilterType::New();

  m_NarrowBanding = false;
  m_InputNarrowBandwidth = 12.0;
  m_OutputNarrowBandwidth = 12.0;
  m_InputNarrowBand = ITK_NULLPTR;
  m_OutputNarrowBand = ITK_NULLPTR;
}

/*
 * Set the input narrowband container.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::SetInputNarrowBand(
  NodeContainer *ptr)
{
  if ( m_InputNarrowBand != ptr )
    {
    m_InputNarrowBand = ptr;
    this->Modified();
    }
}

/**
 * PrintSelf method.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);
  os << indent << "Level set value: " << m_LevelSetValue << std::endl;
  os << indent << "Narrowbanding: " << m_NarrowBanding << std::endl;
  os << indent << "Input narrow bandwidth: " << m_InputNarrowBandwidth;
  os << std::endl;
  os << indent << "Output narrow bandwidth: " << m_OutputNarrowBandwidth;
  os << std::endl;
  os << indent << "Input narrow band: " << m_InputNarrowBand.GetPointer();
  os << std::endl;
  os << indent << "Output narrow band: " << m_OutputNarrowBand.GetPointer();
  os << std::endl;
}

/*
 * GenerateInputRequestedRegion method.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::GenerateInputRequestedRegion()
{
  // use the default implementation.
  this->Superclass::GenerateInputRequestedRegion();
}

/*
 * EnlargeOutputRequestedRegion method.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::EnlargeOutputRequestedRegion(
  DataObject *output)
{
  // this filter requires the all of the output image to be in
  // the buffer
  TLevelSet *imgData;

  imgData = dynamic_cast< TLevelSet * >( output );
  if ( imgData )
    {
    imgData->SetRequestedRegionToLargestPossibleRegion();
    }
  else
    {
    // pointer could not be cast to TLevelSet *
    itkWarningMacro( << "itk::ReinitializeLevelSetImageFilter"
                     << "::EnlargeOutputRequestedRegion cannot cast "
                     << typeid( output ).name() << " to "
                     << typeid( TLevelSet * ).name() );
    }
}

/*
 * Allocate/initialize memory.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::AllocateOutput()
{
  LevelSetPointer outputPtr = this->GetOutput();

  // allocate the output buffer memory
  outputPtr->SetBufferedRegion(
    outputPtr->GetRequestedRegion() );
  outputPtr->Allocate();

  // set the marcher output size
  this->m_Marcher->SetOutputRegion( outputPtr->GetRequestedRegion() );
  this->m_Marcher->SetOutputOrigin( this->GetInput()->GetOrigin() );
  this->m_Marcher->SetOutputSpacing( this->GetInput()->GetSpacing() );
  this->m_Marcher->SetOutputDirection( this->GetInput()->GetDirection() );
}

/*
 * Generate the output data.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::GenerateData()
{
  this->AllocateOutput();

  if ( m_NarrowBanding )
    {
    this->GenerateDataNarrowBand();
    }
  else
    {
    this->GenerateDataFull();
    }
}

/*
 * Generate the output data - full set version.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::GenerateDataFull()
{
  LevelSetConstPointer inputPtr = this->GetInput();
  LevelSetPointer      outputPtr = this->GetOutput();
  LevelSetPointer      tempLevelSet = m_Marcher->GetOutput();

  // define iterators
  typedef ImageRegionIterator< LevelSetImageType >      IteratorType;
  typedef ImageRegionConstIterator< LevelSetImageType > ConstIteratorType;

  ConstIteratorType inputIt( inputPtr,
                             inputPtr->GetBufferedRegion() );
  IteratorType outputIt( outputPtr,
                         outputPtr->GetBufferedRegion() );

  IteratorType tempIt;

  this->UpdateProgress(0.0);

  // locate the level set
  m_Locator->SetInputLevelSet(inputPtr);
  m_Locator->SetLevelSetValue(m_LevelSetValue);
  m_Locator->Locate();

  this->UpdateProgress(0.33);

  // march outward
  m_Marcher->SetTrialPoints( m_Locator->GetOutsidePoints() );
  m_Marcher->Update();

  tempIt = IteratorType( tempLevelSet,
                         tempLevelSet->GetBufferedRegion() );

  double value;

  while ( !inputIt.IsAtEnd() )
    {
    value = (double)inputIt.Get();
    if ( value - m_LevelSetValue > 0 )
      {
      outputIt.Set( tempIt.Get() );
      }

    ++inputIt;
    ++outputIt;
    ++tempIt;
    }

  this->UpdateProgress(0.66);

  // march inward
  m_Marcher->SetTrialPoints( m_Locator->GetInsidePoints() );
  m_Marcher->Update();

  inputIt.GoToBegin();
  outputIt.GoToBegin();
  tempIt.GoToBegin();

  while ( !inputIt.IsAtEnd() )
    {
    value = (double)inputIt.Get();
    if ( value - m_LevelSetValue <= 0 )
      {
      value = (double)tempIt.Get();
      outputIt.Set(-1.0 * value);
      }

    ++inputIt;
    ++outputIt;
    ++tempIt;
    }
}

/*
 * Generate output data - narrowband version.
 */
template< typename TLevelSet >
void
ReinitializeLevelSetImageFilter< TLevelSet >
::GenerateDataNarrowBand()
{
  LevelSetConstPointer inputPtr = this->GetInput();
  LevelSetPointer      outputPtr = this->GetOutput();
  LevelSetPointer      tempLevelSet = m_Marcher->GetOutput();

  // define iterators
  typedef ImageRegionIterator< LevelSetImageType >      IteratorType;
  typedef ImageRegionConstIterator< LevelSetImageType > ConstIteratorType;

  ConstIteratorType inputIt( inputPtr,
                             inputPtr->GetBufferedRegion() );

  IteratorType outputIt( outputPtr,
                         outputPtr->GetBufferedRegion() );

  PixelType posInfinity;
  PixelType negInfinity;

  posInfinity = NumericTraits< PixelType >::max();
  negInfinity = NumericTraits< PixelType >::NonpositiveMin();

  // set all internal pixels to minus infinity and
  // all external pixels to positive infinity
  double value;

  while ( !inputIt.IsAtEnd() )
    {
    value = (double)inputIt.Get();
    if ( value - m_LevelSetValue <= 0 )
      {
      outputIt.Set(negInfinity);
      }
    else
      {
      outputIt.Set(posInfinity);
      }

    ++inputIt;
    ++outputIt;
    }

  // create a new output narrowband container
  m_OutputNarrowBand = NodeContainer::New();

  this->UpdateProgress(0.0);

  // locate the level set
  m_Locator->SetInputLevelSet(inputPtr);
  m_Locator->SetLevelSetValue(m_LevelSetValue);

  if ( m_NarrowBanding && m_InputNarrowBand )
    {
    m_Locator->NarrowBandingOn();
    m_Locator->SetNarrowBandwidth(m_InputNarrowBandwidth);
    m_Locator->SetInputNarrowBand(m_InputNarrowBand);
    }
  else
    {
    m_Locator->NarrowBandingOff();
    }

  m_Locator->Locate();

  this->UpdateProgress(0.33);

  // march outward
  double stoppingValue = ( m_OutputNarrowBandwidth / 2.0 ) + 2.0;
  m_Marcher->SetStoppingValue(stoppingValue);
  m_Marcher->CollectPointsOn();
  m_Marcher->SetTrialPoints( m_Locator->GetOutsidePoints() );
  m_Marcher->Update();

  NodeContainerPointer procPoints = m_Marcher->GetProcessedPoints();

  typename NodeContainer::ConstIterator pointsIt;
  typename NodeContainer::ConstIterator pointsEnd;

  pointsIt = procPoints->Begin();
  pointsEnd = procPoints->End();

  NodeType  node;
  PixelType inPixel;

  for (; pointsIt != pointsEnd; ++pointsIt )
    {
    node = pointsIt.Value();
    inPixel = inputPtr->GetPixel( node.GetIndex() );

    value = (double)inPixel;
    if ( value - m_LevelSetValue > 0 )
      {
      inPixel = tempLevelSet->GetPixel( node.GetIndex() );
      outputPtr->SetPixel(node.GetIndex(), inPixel);
      m_OutputNarrowBand->InsertElement(m_OutputNarrowBand->Size(), node);
      }
    } // end for loop

  this->UpdateProgress(0.66);

  // march inward
  m_Marcher->SetTrialPoints( m_Locator->GetInsidePoints() );
  m_Marcher->Update();

  procPoints = m_Marcher->GetProcessedPoints();
  pointsIt = procPoints->Begin();
  pointsEnd = procPoints->End();

  for (; pointsIt != pointsEnd; ++pointsIt )
    {
    node = pointsIt.Value();
    inPixel = inputPtr->GetPixel( node.GetIndex() );

    value = (double)inPixel;
    if ( value - m_LevelSetValue <= 0 )
      {
      inPixel = tempLevelSet->GetPixel( node.GetIndex() );
      value = (double)inPixel;
      inPixel =  -1.0 * value;
      outputPtr->SetPixel(node.GetIndex(), inPixel);
      node.SetValue(node.GetValue() * -1.0);
      m_OutputNarrowBand->InsertElement(m_OutputNarrowBand->Size(), node);
      }
    } // end for loop
}
} // namespace itk

#endif
<|endoftext|>


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

#include "chrome/browser/gtk/gtk_chrome_link_button.h"

#include "base/logging.h"

static const char* kLinkMarkup = "%s";

namespace {

// Set the GTK style on our custom link button. We don't want any border around
// the link text.
void SetLinkButtonStyle() {
  static bool style_was_set = false;

  if (style_was_set)
    return;
  style_was_set = true;

  gtk_rc_parse_string(
      "style \"chrome-link-button\" {"
      "  GtkButton::inner-border = {0, 0, 0, 0}"
      "  xthickness = 0"
      "  ythickness = 0"
      "}"
      "widget \"*chrome-link-button\" style \"chrome-link-button\"");
}

}  // namespace

G_BEGIN_DECLS
G_DEFINE_TYPE(GtkChromeLinkButton, gtk_chrome_link_button, GTK_TYPE_BUTTON)

static gboolean gtk_chrome_link_button_expose(GtkWidget* widget,
                                              GdkEventExpose* event) {
  GtkChromeLinkButton* button = GTK_CHROME_LINK_BUTTON(widget);
  GtkWidget* label = button->label;

  if (GTK_WIDGET_STATE(widget) == GTK_STATE_ACTIVE && button->is_blue) {
    gtk_label_set_markup(GTK_LABEL(label), button->red_markup);
    button->is_blue = FALSE;
  } else if (GTK_WIDGET_STATE(widget) != GTK_STATE_ACTIVE && !button->is_blue) {
    gtk_label_set_markup(GTK_LABEL(label), button->blue_markup);
    button->is_blue = TRUE;
  }

  // Draw the link inside the button.
  gtk_container_propagate_expose(GTK_CONTAINER(widget), label, event);

  // Draw the focus rectangle.
  if (GTK_WIDGET_HAS_FOCUS(widget)) {
    gtk_paint_focus(widget->style, widget->window,
                    static_cast<GtkStateType>(GTK_WIDGET_STATE(widget)),
                    &event->area, widget, NULL,
                    widget->allocation.x, widget->allocation.y,
                    widget->allocation.width, widget->allocation.height);
  }

  return TRUE;
}

static gboolean gtk_chrome_link_button_button_press(GtkWidget* widget,
                                                    GdkEventButton* event) {
  GtkButton* button;

  if (event->type == GDK_BUTTON_PRESS) {
    button = GTK_BUTTON(widget);

    if (button->focus_on_click && !GTK_WIDGET_HAS_FOCUS (widget))
      gtk_widget_grab_focus(widget);

    if (event->button == 1 || event->button == 2)
      gtk_button_pressed(button);
  }

  return TRUE;
}

static gboolean gtk_chrome_link_button_button_release(GtkWidget* widget,
                                                      GdkEventButton* event) {
  GtkButton* button = GTK_BUTTON(widget);
  GtkChromeLinkButton* link_button = GTK_CHROME_LINK_BUTTON(widget);

  free(link_button->click_button_event);
  link_button->click_button_event = static_cast<GdkEventButton*>(
      malloc(sizeof(GdkEventButton)));
  *link_button->click_button_event = *event;

  if (event->button == 1 || event->button == 2)
    gtk_button_released(button);

  return TRUE;
}

static void gtk_chrome_link_button_enter(GtkButton* button) {
  GtkWidget* widget = GTK_WIDGET(button);
  GtkChromeLinkButton* link_button = GTK_CHROME_LINK_BUTTON(button);
  gdk_window_set_cursor(widget->window, link_button->hand_cursor);
}

static void gtk_chrome_link_button_leave(GtkButton* button) {
  GtkWidget* widget = GTK_WIDGET(button);
  GtkChromeLinkButton* link_button = GTK_CHROME_LINK_BUTTON(button);
  gdk_window_set_cursor(widget->window, NULL);
  free(link_button->click_button_event);
  link_button->click_button_event = NULL;
}

static void gtk_chrome_link_button_destroy(GtkObject* object) {
  GtkChromeLinkButton* button = GTK_CHROME_LINK_BUTTON(object);
  if (button->blue_markup) {
    g_free(button->blue_markup);
    button->blue_markup = NULL;
  }
  if (button->red_markup) {
    g_free(button->red_markup);
    button->red_markup = NULL;
  }
  if (button->hand_cursor) {
    gdk_cursor_unref(button->hand_cursor);
    button->hand_cursor = NULL;
  }
  free(button->click_button_event);
  button->click_button_event = NULL;

  GTK_OBJECT_CLASS(gtk_chrome_link_button_parent_class)->destroy(object);
}

static void gtk_chrome_link_button_class_init(
    GtkChromeLinkButtonClass* link_button_class) {
  GtkWidgetClass* widget_class =
      reinterpret_cast<GtkWidgetClass*>(link_button_class);
  GtkButtonClass* button_class =
      reinterpret_cast<GtkButtonClass*>(link_button_class);
  GtkObjectClass* object_class =
      reinterpret_cast<GtkObjectClass*>(link_button_class);
  widget_class->expose_event = &gtk_chrome_link_button_expose;
  widget_class->button_press_event = &gtk_chrome_link_button_button_press;
  widget_class->button_release_event = &gtk_chrome_link_button_button_release;
  button_class->enter = &gtk_chrome_link_button_enter;
  button_class->leave = &gtk_chrome_link_button_leave;
  object_class->destroy = &gtk_chrome_link_button_destroy;
}

static void gtk_chrome_link_button_init(GtkChromeLinkButton* button) {
  SetLinkButtonStyle();

  // We put a label in a button so we can connect to the click event. We don't
  // let the button draw itself; catch all expose events to the button and pass
  // them through to the label.
  button->label = gtk_label_new(NULL);
  button->blue_markup = NULL;
  button->red_markup = NULL;
  button->is_blue = TRUE;
  button->hand_cursor = gdk_cursor_new(GDK_HAND2);
  button->click_button_event = NULL;

  gtk_container_add(GTK_CONTAINER(button), button->label);
  gtk_widget_set_name(GTK_WIDGET(button), "chrome-link-button");
  gtk_widget_set_app_paintable(GTK_WIDGET(button), TRUE);
}

static void gtk_chrome_link_button_set_text(GtkChromeLinkButton* button,
                                            const char* text) {
  // We should have only been called once or we'd leak the markups.
  DCHECK(!button->blue_markup && !button->red_markup);

  button->blue_markup = g_markup_printf_escaped(kLinkMarkup, "blue", text);
  button->red_markup = g_markup_printf_escaped(kLinkMarkup, "red", text);
  gtk_label_set_markup(GTK_LABEL(button->label), button->blue_markup);
  button->is_blue = TRUE;
}

GtkWidget* gtk_chrome_link_button_new(const char* text) {
  GtkWidget* lb = GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_LINK_BUTTON, NULL));
  gtk_chrome_link_button_set_text(GTK_CHROME_LINK_BUTTON(lb), text);
  return lb;
}

const GdkEventButton* gtk_chrome_link_button_get_event_for_click(
    GtkChromeLinkButton* button) {
  return button->click_button_event;
}

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

#include "chrome/browser/gtk/gtk_chrome_link_button.h"

#include <stdlib.h>

#include "base/logging.h"

static const char* kLinkMarkup = "%s";

namespace {

// Set the GTK style on our custom link button. We don't want any border around
// the link text.
void SetLinkButtonStyle() {
  static bool style_was_set = false;

  if (style_was_set)
    return;
  style_was_set = true;

  gtk_rc_parse_string(
      "style \"chrome-link-button\" {"
      "  GtkButton::inner-border = {0, 0, 0, 0}"
      "  xthickness = 0"
      "  ythickness = 0"
      "}"
      "widget \"*chrome-link-button\" style \"chrome-link-button\"");
}

}  // namespace

G_BEGIN_DECLS
G_DEFINE_TYPE(GtkChromeLinkButton, gtk_chrome_link_button, GTK_TYPE_BUTTON)

static gboolean gtk_chrome_link_button_expose(GtkWidget* widget,
                                              GdkEventExpose* event) {
  GtkChromeLinkButton* button = GTK_CHROME_LINK_BUTTON(widget);
  GtkWidget* label = button->label;

  if (GTK_WIDGET_STATE(widget) == GTK_STATE_ACTIVE && button->is_blue) {
    gtk_label_set_markup(GTK_LABEL(label), button->red_markup);
    button->is_blue = FALSE;
  } else if (GTK_WIDGET_STATE(widget) != GTK_STATE_ACTIVE && !button->is_blue) {
    gtk_label_set_markup(GTK_LABEL(label), button->blue_markup);
    button->is_blue = TRUE;
  }

  // Draw the link inside the button.
  gtk_container_propagate_expose(GTK_CONTAINER(widget), label, event);

  // Draw the focus rectangle.
  if (GTK_WIDGET_HAS_FOCUS(widget)) {
    gtk_paint_focus(widget->style, widget->window,
                    static_cast<GtkStateType>(GTK_WIDGET_STATE(widget)),
                    &event->area, widget, NULL,
                    widget->allocation.x, widget->allocation.y,
                    widget->allocation.width, widget->allocation.height);
  }

  return TRUE;
}

static gboolean gtk_chrome_link_button_button_press(GtkWidget* widget,
                                                    GdkEventButton* event) {
  GtkButton* button;

  if (event->type == GDK_BUTTON_PRESS) {
    button = GTK_BUTTON(widget);

    if (button->focus_on_click && !GTK_WIDGET_HAS_FOCUS (widget))
      gtk_widget_grab_focus(widget);

    if (event->button == 1 || event->button == 2)
      gtk_button_pressed(button);
  }

  return TRUE;
}

static gboolean gtk_chrome_link_button_button_release(GtkWidget* widget,
                                                      GdkEventButton* event) {
  GtkButton* button = GTK_BUTTON(widget);
  GtkChromeLinkButton* link_button = GTK_CHROME_LINK_BUTTON(widget);

  free(link_button->click_button_event);
  link_button->click_button_event = static_cast<GdkEventButton*>(
      malloc(sizeof(GdkEventButton)));
  *link_button->click_button_event = *event;

  if (event->button == 1 || event->button == 2)
    gtk_button_released(button);

  return TRUE;
}

static void gtk_chrome_link_button_enter(GtkButton* button) {
  GtkWidget* widget = GTK_WIDGET(button);
  GtkChromeLinkButton* link_button = GTK_CHROME_LINK_BUTTON(button);
  gdk_window_set_cursor(widget->window, link_button->hand_cursor);
}

static void gtk_chrome_link_button_leave(GtkButton* button) {
  GtkWidget* widget = GTK_WIDGET(button);
  GtkChromeLinkButton* link_button = GTK_CHROME_LINK_BUTTON(button);
  gdk_window_set_cursor(widget->window, NULL);
  free(link_button->click_button_event);
  link_button->click_button_event = NULL;
}

static void gtk_chrome_link_button_destroy(GtkObject* object) {
  GtkChromeLinkButton* button = GTK_CHROME_LINK_BUTTON(object);
  if (button->blue_markup) {
    g_free(button->blue_markup);
    button->blue_markup = NULL;
  }
  if (button->red_markup) {
    g_free(button->red_markup);
    button->red_markup = NULL;
  }
  if (button->hand_cursor) {
    gdk_cursor_unref(button->hand_cursor);
    button->hand_cursor = NULL;
  }
  free(button->click_button_event);
  button->click_button_event = NULL;

  GTK_OBJECT_CLASS(gtk_chrome_link_button_parent_class)->destroy(object);
}

static void gtk_chrome_link_button_class_init(
    GtkChromeLinkButtonClass* link_button_class) {
  GtkWidgetClass* widget_class =
      reinterpret_cast<GtkWidgetClass*>(link_button_class);
  GtkButtonClass* button_class =
      reinterpret_cast<GtkButtonClass*>(link_button_class);
  GtkObjectClass* object_class =
      reinterpret_cast<GtkObjectClass*>(link_button_class);
  widget_class->expose_event = &gtk_chrome_link_button_expose;
  widget_class->button_press_event = &gtk_chrome_link_button_button_press;
  widget_class->button_release_event = &gtk_chrome_link_button_button_release;
  button_class->enter = &gtk_chrome_link_button_enter;
  button_class->leave = &gtk_chrome_link_button_leave;
  object_class->destroy = &gtk_chrome_link_button_destroy;
}

static void gtk_chrome_link_button_init(GtkChromeLinkButton* button) {
  SetLinkButtonStyle();

  // We put a label in a button so we can connect to the click event. We don't
  // let the button draw itself; catch all expose events to the button and pass
  // them through to the label.
  button->label = gtk_label_new(NULL);
  button->blue_markup = NULL;
  button->red_markup = NULL;
  button->is_blue = TRUE;
  button->hand_cursor = gdk_cursor_new(GDK_HAND2);
  button->click_button_event = NULL;

  gtk_container_add(GTK_CONTAINER(button), button->label);
  gtk_widget_set_name(GTK_WIDGET(button), "chrome-link-button");
  gtk_widget_set_app_paintable(GTK_WIDGET(button), TRUE);
}

static void gtk_chrome_link_button_set_text(GtkChromeLinkButton* button,
                                            const char* text) {
  // We should have only been called once or we'd leak the markups.
  DCHECK(!button->blue_markup && !button->red_markup);

  button->blue_markup = g_markup_printf_escaped(kLinkMarkup, "blue", text);
  button->red_markup = g_markup_printf_escaped(kLinkMarkup, "red", text);
  gtk_label_set_markup(GTK_LABEL(button->label), button->blue_markup);
  button->is_blue = TRUE;
}

GtkWidget* gtk_chrome_link_button_new(const char* text) {
  GtkWidget* lb = GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_LINK_BUTTON, NULL));
  gtk_chrome_link_button_set_text(GTK_CHROME_LINK_BUTTON(lb), text);
  return lb;
}

const GdkEventButton* gtk_chrome_link_button_get_event_for_click(
    GtkChromeLinkButton* button) {
  return button->click_button_event;
}

G_END_DECLS
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

// Borrowed from Chromium's src/base/threading/thread_checker_unittest.cc.

#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/thread.h"
#include "webrtc/base/thread_checker.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/test/testsupport/gtest_disable.h"

// Duplicated from base/threading/thread_checker.h so that we can be
// good citizens there and undef the macro.
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
#define ENABLE_THREAD_CHECKER 1
#else
#define ENABLE_THREAD_CHECKER 0
#endif

namespace rtc {

namespace {

// Simple class to exercise the basics of ThreadChecker.
// Both the destructor and DoStuff should verify that they were
// called on the same thread as the constructor.
class ThreadCheckerClass : public ThreadChecker {
 public:
  ThreadCheckerClass() {}

  // Verifies that it was called on the same thread as the constructor.
  void DoStuff() {
    DCHECK(CalledOnValidThread());
  }

  void DetachFromThread() {
    ThreadChecker::DetachFromThread();
  }

  static void MethodOnDifferentThreadImpl();
  static void DetachThenCallFromDifferentThreadImpl();

 private:
  DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
};

// Calls ThreadCheckerClass::DoStuff on another thread.
class CallDoStuffOnThread : public Thread {
 public:
  explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
      : Thread(),
        thread_checker_class_(thread_checker_class) {
    SetName("call_do_stuff_on_thread", NULL);
  }

  virtual void Run() OVERRIDE {
    thread_checker_class_->DoStuff();
  }

  // New method. Needed since Thread::Join is protected, and it is called by
  // the TEST.
  void Join() {
    Thread::Join();
  }

 private:
  ThreadCheckerClass* thread_checker_class_;

  DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
};

// Deletes ThreadCheckerClass on a different thread.
class DeleteThreadCheckerClassOnThread : public Thread {
 public:
  explicit DeleteThreadCheckerClassOnThread(
      ThreadCheckerClass* thread_checker_class)
      : Thread(),
        thread_checker_class_(thread_checker_class) {
    SetName("delete_thread_checker_class_on_thread", NULL);
  }

  virtual void Run() OVERRIDE {
    thread_checker_class_.reset();
  }

  // New method. Needed since Thread::Join is protected, and it is called by
  // the TEST.
  void Join() {
    Thread::Join();
  }

 private:
  scoped_ptr<ThreadCheckerClass> thread_checker_class_;

  DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
};

}  // namespace

TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
  scoped_ptr<ThreadCheckerClass> thread_checker_class(
      new ThreadCheckerClass);

  // Verify that DoStuff doesn't assert.
  thread_checker_class->DoStuff();

  // Verify that the destructor doesn't assert.
  thread_checker_class.reset();
}

TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
  scoped_ptr<ThreadCheckerClass> thread_checker_class(
      new ThreadCheckerClass);

  // Verify that the destructor doesn't assert
  // when called on a different thread.
  DeleteThreadCheckerClassOnThread delete_on_thread(
      thread_checker_class.release());

  delete_on_thread.Start();
  delete_on_thread.Join();
}

TEST(ThreadCheckerTest, DetachFromThread) {
  scoped_ptr<ThreadCheckerClass> thread_checker_class(
      new ThreadCheckerClass);

  // Verify that DoStuff doesn't assert when called on a different thread after
  // a call to DetachFromThread.
  thread_checker_class->DetachFromThread();
  CallDoStuffOnThread call_on_thread(thread_checker_class.get());

  call_on_thread.Start();
  call_on_thread.Join();
}

#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER

void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
  scoped_ptr<ThreadCheckerClass> thread_checker_class(
      new ThreadCheckerClass);

  // DoStuff should assert in debug builds only when called on a
  // different thread.
  CallDoStuffOnThread call_on_thread(thread_checker_class.get());

  call_on_thread.Start();
  call_on_thread.Join();
}

#if ENABLE_THREAD_CHECKER
TEST(ThreadCheckerDeathTest,
     DISABLED_MethodNotAllowedOnDifferentThreadInDebug) {
  ASSERT_DEATH({
      ThreadCheckerClass::MethodOnDifferentThreadImpl();
    }, "");
}
#else
TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
  ThreadCheckerClass::MethodOnDifferentThreadImpl();
}
#endif  // ENABLE_THREAD_CHECKER

void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
  scoped_ptr<ThreadCheckerClass> thread_checker_class(
      new ThreadCheckerClass);

  // DoStuff doesn't assert when called on a different thread
  // after a call to DetachFromThread.
  thread_checker_class->DetachFromThread();
  CallDoStuffOnThread call_on_thread(thread_checker_class.get());

  call_on_thread.Start();
  call_on_thread.Join();

  // DoStuff should assert in debug builds only after moving to
  // another thread.
  thread_checker_class->DoStuff();
}

#if ENABLE_THREAD_CHECKER
TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
  ASSERT_DEATH({
    ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
    }, "");
}
#else
TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
  ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
}
#endif  // ENABLE_THREAD_CHECKER

#endif  // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER

// Just in case we ever get lumped together with other compilation units.
#undef ENABLE_THREAD_CHECKER

}  // namespace rtc
<commit_msg>Re-enable ThreadCheckerDeathTest.MethodNotAllowedOnDifferentThreadInDebug (missed when enabling other base tests).<commit_after>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

// Borrowed from Chromium's src/base/threading/thread_checker_unittest.cc.

#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/thread.h"
#include "webrtc/base/thread_checker.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/test/testsupport/gtest_disable.h"

// Duplicated from base/threading/thread_checker.h so that we can be
// good citizens there and undef the macro.
#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
#define ENABLE_THREAD_CHECKER 1
#else
#define ENABLE_THREAD_CHECKER 0
#endif

namespace rtc {

namespace {

// Simple class to exercise the basics of ThreadChecker.
// Both the destructor and DoStuff should verify that they were
// called on the same thread as the constructor.
class ThreadCheckerClass : public ThreadChecker {
 public:
  ThreadCheckerClass() {}

  // Verifies that it was called on the same thread as the constructor.
  void DoStuff() {
    DCHECK(CalledOnValidThread());
  }

  void DetachFromThread() {
    ThreadChecker::DetachFromThread();
  }

  static void MethodOnDifferentThreadImpl();
  static void DetachThenCallFromDifferentThreadImpl();

 private:
  DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
};

// Calls ThreadCheckerClass::DoStuff on another thread.
class CallDoStuffOnThread : public Thread {
 public:
  explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
      : Thread(),
        thread_checker_class_(thread_checker_class) {
    SetName("call_do_stuff_on_thread", NULL);
  }

  virtual void Run() OVERRIDE {
    thread_checker_class_->DoStuff();
  }

  // New method. Needed since Thread::Join is protected, and it is called by
  // the TEST.
  void Join() {
    Thread::Join();
  }

 private:
  ThreadCheckerClass* thread_checker_class_;

  DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
};

// Deletes ThreadCheckerClass on a different thread.
class DeleteThreadCheckerClassOnThread : public Thread {
 public:
  explicit DeleteThreadCheckerClassOnThread(
      ThreadCheckerClass* thread_checker_class)
      : Thread(),
        thread_checker_class_(thread_checker_class) {
    SetName("delete_thread_checker_class_on_thread", NULL);
  }

  virtual void Run() OVERRIDE {
    thread_checker_class_.reset();
  }

  // New method. Needed since Thread::Join is protected, and it is called by
  // the TEST.
  void Join() {
    Thread::Join();
  }

 private:
  scoped_ptr<ThreadCheckerClass> thread_checker_class_;

  DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
};

}  // namespace

TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
  scoped_ptr<ThreadCheckerClass> thread_checker_class(
      new ThreadCheckerClass);

  // Verify that DoStuff doesn't assert.
  thread_checker_class->DoStuff();

  // Verify that the destructor doesn't assert.
  thread_checker_class.reset();
}

TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
  scoped_ptr<ThreadCheckerClass> thread_checker_class(
      new ThreadCheckerClass);

  // Verify that the destructor doesn't assert
  // when called on a different thread.
  DeleteThreadCheckerClassOnThread delete_on_thread(
      thread_checker_class.release());

  delete_on_thread.Start();
  delete_on_thread.Join();
}

TEST(ThreadCheckerTest, DetachFromThread) {
  scoped_ptr<ThreadCheckerClass> thread_checker_class(
      new ThreadCheckerClass);

  // Verify that DoStuff doesn't assert when called on a different thread after
  // a call to DetachFromThread.
  thread_checker_class->DetachFromThread();
  CallDoStuffOnThread call_on_thread(thread_checker_class.get());

  call_on_thread.Start();
  call_on_thread.Join();
}

#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER

void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
  scoped_ptr<ThreadCheckerClass> thread_checker_class(
      new ThreadCheckerClass);

  // DoStuff should assert in debug builds only when called on a
  // different thread.
  CallDoStuffOnThread call_on_thread(thread_checker_class.get());

  call_on_thread.Start();
  call_on_thread.Join();
}

#if ENABLE_THREAD_CHECKER
TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
  ASSERT_DEATH({
      ThreadCheckerClass::MethodOnDifferentThreadImpl();
    }, "");
}
#else
TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
  ThreadCheckerClass::MethodOnDifferentThreadImpl();
}
#endif  // ENABLE_THREAD_CHECKER

void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
  scoped_ptr<ThreadCheckerClass> thread_checker_class(
      new ThreadCheckerClass);

  // DoStuff doesn't assert when called on a different thread
  // after a call to DetachFromThread.
  thread_checker_class->DetachFromThread();
  CallDoStuffOnThread call_on_thread(thread_checker_class.get());

  call_on_thread.Start();
  call_on_thread.Join();

  // DoStuff should assert in debug builds only after moving to
  // another thread.
  thread_checker_class->DoStuff();
}

#if ENABLE_THREAD_CHECKER
TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
  ASSERT_DEATH({
    ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
    }, "");
}
#else
TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
  ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
}
#endif  // ENABLE_THREAD_CHECKER

#endif  // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER

// Just in case we ever get lumped together with other compilation units.
#undef ENABLE_THREAD_CHECKER

}  // namespace rtc
<|endoftext|>


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

#include "chrome/browser/views/about_network_dialog.h"

#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/views/standard_layout.h"
#include "chrome/views/grid_layout.h"
#include "chrome/views/controls/button/text_button.h"
#include "chrome/views/controls/text_field.h"
#include "chrome/views/window/window.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_job_tracker.h"

namespace {

// We don't localize this UI since this is a developer-only feature.
const wchar_t kStartTrackingLabel[] = L"Start tracking";
const wchar_t kStopTrackingLabel[] = L"Stop tracking";
const wchar_t kShowCurrentLabel[] = L"Show Current";
const wchar_t kClearLabel[] = L"Clear";

// The singleton dialog box. This is non-NULL when a dialog is active so we
// know not to create a new one.
AboutNetworkDialog* active_dialog = NULL;

// Returns a string representing the URL, handling the case where the spec
// is invalid.
std::wstring StringForURL(const GURL& url) {
  if (url.is_valid())
    return UTF8ToWide(url.spec());
  return UTF8ToWide(url.possibly_invalid_spec()) + L" (invalid)";
}

std::wstring URLForJob(URLRequestJob* job) {
  URLRequest* request = job->request();
  if (request)
    return StringForURL(request->url());
  return std::wstring(L"(orphaned)");
}

// JobTracker ------------------------------------------------------------------

// A JobTracker is allocated to monitor network jobs running on the IO
// thread.  This allows the NetworkStatusView to remain single-threaded.
class JobTracker : public URLRequestJobTracker::JobObserver,
                   public base::RefCountedThreadSafe<JobTracker> {
 public:
  JobTracker(AboutNetworkDialog* view);
  ~JobTracker();

  // Called by the NetworkStatusView on the main application thread.
  void StartTracking();
  void StopTracking();
  void ReportStatus();

  // URLRequestJobTracker::JobObserver methods (called on the IO thread):
  virtual void OnJobAdded(URLRequestJob* job);
  virtual void OnJobRemoved(URLRequestJob* job);
  virtual void OnJobDone(URLRequestJob* job, const URLRequestStatus& status);
  virtual void OnJobRedirect(URLRequestJob* job, const GURL& location,
                             int status_code);
  virtual void OnBytesRead(URLRequestJob* job, int byte_count);

  // The JobTracker may be deleted after NetworkStatusView is deleted.
  void DetachView() { view_ = NULL; }

 private:
  void InvokeOnIOThread(void (JobTracker::*method)());

  // Called on the IO thread
  void OnStartTracking();
  void OnStopTracking();
  void OnReportStatus();
  void AppendText(const std::wstring& text);

  // Called on the main thread
  void OnAppendText(const std::wstring& text);

  AboutNetworkDialog* view_;
  MessageLoop* view_message_loop_;
};

// main thread:
JobTracker::JobTracker(AboutNetworkDialog* view)
    : view_(view),
      view_message_loop_(MessageLoop::current()) {
}

JobTracker::~JobTracker() {
}

// main thread:
void JobTracker::InvokeOnIOThread(void (JobTracker::*m)()) {
  base::Thread* thread = g_browser_process->io_thread();
  if (!thread)
    return;
  thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, m));
}

// main thread:
void JobTracker::StartTracking() {
  DCHECK(MessageLoop::current() == view_message_loop_);
  DCHECK(view_);
  InvokeOnIOThread(&JobTracker::OnStartTracking);
}

// main thread:
void JobTracker::StopTracking() {
  DCHECK(MessageLoop::current() == view_message_loop_);
  // The tracker should not be deleted before it is removed from observer
  // list.
  AddRef();
  InvokeOnIOThread(&JobTracker::OnStopTracking);
}

// main thread:
void JobTracker::ReportStatus() {
  DCHECK(MessageLoop::current() == view_message_loop_);
  InvokeOnIOThread(&JobTracker::OnReportStatus);
}

// main thread:
void JobTracker::OnAppendText(const std::wstring& text) {
  DCHECK(MessageLoop::current() == view_message_loop_);
  if (view_ && view_->tracking())
    view_->AppendText(text);
}

// IO thread:
void JobTracker::AppendText(const std::wstring& text) {
  DCHECK(MessageLoop::current() != view_message_loop_);
  view_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
      this, &JobTracker::OnAppendText, text));
}

// IO thread:
void JobTracker::OnStartTracking() {
  DCHECK(MessageLoop::current() != view_message_loop_);
  g_url_request_job_tracker.AddObserver(this);
}

// IO thread:
void JobTracker::OnStopTracking() {
  DCHECK(MessageLoop::current() != view_message_loop_);
  g_url_request_job_tracker.RemoveObserver(this);
  // Balance the AddRef() in StopTracking() called in main thread.
  Release();
}

// IO thread:
void JobTracker::OnReportStatus() {
  DCHECK(MessageLoop::current() != view_message_loop_);

  std::wstring text(L"\r\n===== Active Job Summary =====\r\n");

  URLRequestJobTracker::JobIterator begin_job =
      g_url_request_job_tracker.begin();
  URLRequestJobTracker::JobIterator end_job = g_url_request_job_tracker.end();
  int orphaned_count = 0;
  int regular_count = 0;
  for (URLRequestJobTracker::JobIterator cur = begin_job;
       cur != end_job; ++cur) {
    URLRequestJob* job = (*cur);
    URLRequest* request = job->request();
    if (!request) {
      orphaned_count++;
      continue;
    }

    regular_count++;

    // active state
    if (job->is_done())
      text.append(L"  Done:   ");
    else
      text.append(L"  Active: ");

    // URL
    text.append(StringForURL(request->url()));
    text.append(L"\r\n");
  }

  if (regular_count == 0)
    text.append(L"  (No active jobs)\r\n");

  if (orphaned_count) {
    wchar_t buf[64];
    swprintf(buf, arraysize(buf), L"  %d orphaned jobs\r\n", orphaned_count);
    text.append(buf);
  }

  text.append(L"=====\r\n\r\n");
  AppendText(text);
}

// IO thread:
void JobTracker::OnJobAdded(URLRequestJob* job) {
  DCHECK(MessageLoop::current() != view_message_loop_);

  std::wstring text(L"+ New job : ");
  text.append(URLForJob(job));
  text.append(L"\r\n");
  AppendText(text);
}

// IO thread:
void JobTracker::OnJobRemoved(URLRequestJob* job) {
  DCHECK(MessageLoop::current() != view_message_loop_);
}

// IO thread:
void JobTracker::OnJobDone(URLRequestJob* job,
                           const URLRequestStatus& status) {
  DCHECK(MessageLoop::current() != view_message_loop_);

  std::wstring text;
  if (status.is_success()) {
    text.assign(L"- Complete: ");
  } else if (status.status() == URLRequestStatus::CANCELED) {
    text.assign(L"- Canceled: ");
  } else if (status.status() == URLRequestStatus::HANDLED_EXTERNALLY) {
    text.assign(L"- Handled externally: ");
  } else {
    wchar_t buf[32];
    swprintf(buf, arraysize(buf), L"Failed with %d: ", status.os_error());
    text.assign(buf);
  }

  text.append(URLForJob(job));
  text.append(L"\r\n");
  AppendText(text);
}

// IO thread:
void JobTracker::OnJobRedirect(URLRequestJob* job,
                               const GURL& location,
                               int status_code) {
  DCHECK(MessageLoop::current() != view_message_loop_);

  std::wstring text(L"- Redirect: ");
  text.append(URLForJob(job));
  text.append(L"\r\n  ");

  wchar_t buf[16];
  swprintf(buf, arraysize(buf), L"(%d) to: ", status_code);
  text.append(buf);

  text.append(StringForURL(location));
  text.append(L"\r\n");
  AppendText(text);
}

void JobTracker::OnBytesRead(URLRequestJob* job, int byte_count) {
}

// The singleton job tracker associated with the dialog.
JobTracker* tracker = NULL;

}  // namespace

// AboutNetworkDialog ----------------------------------------------------------

AboutNetworkDialog::AboutNetworkDialog() : tracking_(false) {
  SetupControls();
  tracker = new JobTracker(this);
  tracker->AddRef();
}

AboutNetworkDialog::~AboutNetworkDialog() {
  active_dialog = NULL;
  tracker->Release();
  tracker = NULL;
}

// static
void AboutNetworkDialog::RunDialog() {
  if (!active_dialog) {
    active_dialog = new AboutNetworkDialog;
    views::Window::CreateChromeWindow(NULL, gfx::Rect(), active_dialog)->Show();
  } else {
    // TOOD(brettw) it would be nice to focus the existing window.
  }
}

void AboutNetworkDialog::AppendText(const std::wstring& text) {
  text_field_->AppendText(text);
}

void AboutNetworkDialog::SetupControls() {
  views::GridLayout* layout = CreatePanelGridLayout(this);
  SetLayoutManager(layout);

  track_toggle_ = new views::TextButton(this, kStartTrackingLabel);
  show_button_ = new views::TextButton(this, kShowCurrentLabel);
  clear_button_ = new views::TextButton(this, kClearLabel);

  text_field_ = new views::TextField(static_cast<views::TextField::StyleFlags>(
                                     views::TextField::STYLE_MULTILINE));
  text_field_->SetReadOnly(true);

  // TODO(brettw): We may want to add this in the future. It can't be called
  // from here, though, since the hwnd for the field hasn't been created yet.
  //
  // This raises the maximum number of chars from 32K to some large maximum,
  // probably 2GB. 32K is not nearly enough for our use-case.
  //SendMessageW(text_field_->GetNativeComponent(), EM_SETLIMITTEXT, 0, 0);

  static const int first_column_set = 1;
  views::ColumnSet* column_set = layout->AddColumnSet(first_column_set);
  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
                        33.33f, views::GridLayout::FIXED, 0, 0);
  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
                        33.33f, views::GridLayout::FIXED, 0, 0);
  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
                        33.33f, views::GridLayout::FIXED, 0, 0);

  static const int text_column_set = 2;
  column_set = layout->AddColumnSet(text_column_set);
  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
                        100.0f,
                        views::GridLayout::FIXED, 0, 0);

  layout->StartRow(0, first_column_set);
  layout->AddView(track_toggle_);
  layout->AddView(show_button_);
  layout->AddView(clear_button_);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
  layout->StartRow(1.0f, text_column_set);
  layout->AddView(text_field_);
}

gfx::Size AboutNetworkDialog::GetPreferredSize() {
  return gfx::Size(800, 400);
}

views::View* AboutNetworkDialog::GetContentsView() {
  return this;
}

int AboutNetworkDialog::GetDialogButtons() const {
  // Don't want OK or Cancel.
  return 0;
}

std::wstring AboutNetworkDialog::GetWindowTitle() const {
  return L"about:network";
}

bool AboutNetworkDialog::CanResize() const {
  return true;
}

void AboutNetworkDialog::ButtonPressed(views::Button* button) {
  if (button == track_toggle_) {
    if (tracking_) {
      track_toggle_->SetText(kStartTrackingLabel);
      tracking_ = false;
      tracker->StopTracking();
    } else {
      track_toggle_->SetText(kStopTrackingLabel);
      tracking_ = true;
      tracker->StartTracking();
    }
    track_toggle_->SchedulePaint();
  } else if (button == show_button_) {
    tracker->ReportStatus();
  } else if (button == clear_button_) {
    text_field_->SetText(std::wstring());
  }
}
<commit_msg>Fix crash that occurs when the about:network window is closed while still tracking network jobs.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/views/about_network_dialog.h"

#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/views/standard_layout.h"
#include "chrome/views/grid_layout.h"
#include "chrome/views/controls/button/text_button.h"
#include "chrome/views/controls/text_field.h"
#include "chrome/views/window/window.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_job_tracker.h"

namespace {

// We don't localize this UI since this is a developer-only feature.
const wchar_t kStartTrackingLabel[] = L"Start tracking";
const wchar_t kStopTrackingLabel[] = L"Stop tracking";
const wchar_t kShowCurrentLabel[] = L"Show Current";
const wchar_t kClearLabel[] = L"Clear";

// The singleton dialog box. This is non-NULL when a dialog is active so we
// know not to create a new one.
AboutNetworkDialog* active_dialog = NULL;

// Returns a string representing the URL, handling the case where the spec
// is invalid.
std::wstring StringForURL(const GURL& url) {
  if (url.is_valid())
    return UTF8ToWide(url.spec());
  return UTF8ToWide(url.possibly_invalid_spec()) + L" (invalid)";
}

std::wstring URLForJob(URLRequestJob* job) {
  URLRequest* request = job->request();
  if (request)
    return StringForURL(request->url());
  return std::wstring(L"(orphaned)");
}

// JobTracker ------------------------------------------------------------------

// A JobTracker is allocated to monitor network jobs running on the IO
// thread.  This allows the NetworkStatusView to remain single-threaded.
class JobTracker : public URLRequestJobTracker::JobObserver,
                   public base::RefCountedThreadSafe<JobTracker> {
 public:
  JobTracker(AboutNetworkDialog* view);
  ~JobTracker();

  // Called by the NetworkStatusView on the main application thread.
  void StartTracking();
  void StopTracking();
  void ReportStatus();

  // URLRequestJobTracker::JobObserver methods (called on the IO thread):
  virtual void OnJobAdded(URLRequestJob* job);
  virtual void OnJobRemoved(URLRequestJob* job);
  virtual void OnJobDone(URLRequestJob* job, const URLRequestStatus& status);
  virtual void OnJobRedirect(URLRequestJob* job, const GURL& location,
                             int status_code);
  virtual void OnBytesRead(URLRequestJob* job, int byte_count);

  // The JobTracker may be deleted after NetworkStatusView is deleted.
  void DetachView() { view_ = NULL; }

 private:
  void InvokeOnIOThread(void (JobTracker::*method)());

  // Called on the IO thread
  void OnStartTracking();
  void OnStopTracking();
  void OnReportStatus();
  void AppendText(const std::wstring& text);

  // Called on the main thread
  void OnAppendText(const std::wstring& text);

  AboutNetworkDialog* view_;
  MessageLoop* view_message_loop_;
};

// main thread:
JobTracker::JobTracker(AboutNetworkDialog* view)
    : view_(view),
      view_message_loop_(MessageLoop::current()) {
}

JobTracker::~JobTracker() {
}

// main thread:
void JobTracker::InvokeOnIOThread(void (JobTracker::*m)()) {
  base::Thread* thread = g_browser_process->io_thread();
  if (!thread)
    return;
  thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, m));
}

// main thread:
void JobTracker::StartTracking() {
  DCHECK(MessageLoop::current() == view_message_loop_);
  DCHECK(view_);
  InvokeOnIOThread(&JobTracker::OnStartTracking);
}

// main thread:
void JobTracker::StopTracking() {
  DCHECK(MessageLoop::current() == view_message_loop_);
  // The tracker should not be deleted before it is removed from observer
  // list.
  AddRef();
  InvokeOnIOThread(&JobTracker::OnStopTracking);
}

// main thread:
void JobTracker::ReportStatus() {
  DCHECK(MessageLoop::current() == view_message_loop_);
  InvokeOnIOThread(&JobTracker::OnReportStatus);
}

// main thread:
void JobTracker::OnAppendText(const std::wstring& text) {
  DCHECK(MessageLoop::current() == view_message_loop_);
  if (view_ && view_->tracking())
    view_->AppendText(text);
}

// IO thread:
void JobTracker::AppendText(const std::wstring& text) {
  DCHECK(MessageLoop::current() != view_message_loop_);
  view_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
      this, &JobTracker::OnAppendText, text));
}

// IO thread:
void JobTracker::OnStartTracking() {
  DCHECK(MessageLoop::current() != view_message_loop_);
  g_url_request_job_tracker.AddObserver(this);
}

// IO thread:
void JobTracker::OnStopTracking() {
  DCHECK(MessageLoop::current() != view_message_loop_);
  g_url_request_job_tracker.RemoveObserver(this);
  // Balance the AddRef() in StopTracking() called in main thread.
  Release();
}

// IO thread:
void JobTracker::OnReportStatus() {
  DCHECK(MessageLoop::current() != view_message_loop_);

  std::wstring text(L"\r\n===== Active Job Summary =====\r\n");

  URLRequestJobTracker::JobIterator begin_job =
      g_url_request_job_tracker.begin();
  URLRequestJobTracker::JobIterator end_job = g_url_request_job_tracker.end();
  int orphaned_count = 0;
  int regular_count = 0;
  for (URLRequestJobTracker::JobIterator cur = begin_job;
       cur != end_job; ++cur) {
    URLRequestJob* job = (*cur);
    URLRequest* request = job->request();
    if (!request) {
      orphaned_count++;
      continue;
    }

    regular_count++;

    // active state
    if (job->is_done())
      text.append(L"  Done:   ");
    else
      text.append(L"  Active: ");

    // URL
    text.append(StringForURL(request->url()));
    text.append(L"\r\n");
  }

  if (regular_count == 0)
    text.append(L"  (No active jobs)\r\n");

  if (orphaned_count) {
    wchar_t buf[64];
    swprintf(buf, arraysize(buf), L"  %d orphaned jobs\r\n", orphaned_count);
    text.append(buf);
  }

  text.append(L"=====\r\n\r\n");
  AppendText(text);
}

// IO thread:
void JobTracker::OnJobAdded(URLRequestJob* job) {
  DCHECK(MessageLoop::current() != view_message_loop_);

  std::wstring text(L"+ New job : ");
  text.append(URLForJob(job));
  text.append(L"\r\n");
  AppendText(text);
}

// IO thread:
void JobTracker::OnJobRemoved(URLRequestJob* job) {
  DCHECK(MessageLoop::current() != view_message_loop_);
}

// IO thread:
void JobTracker::OnJobDone(URLRequestJob* job,
                           const URLRequestStatus& status) {
  DCHECK(MessageLoop::current() != view_message_loop_);

  std::wstring text;
  if (status.is_success()) {
    text.assign(L"- Complete: ");
  } else if (status.status() == URLRequestStatus::CANCELED) {
    text.assign(L"- Canceled: ");
  } else if (status.status() == URLRequestStatus::HANDLED_EXTERNALLY) {
    text.assign(L"- Handled externally: ");
  } else {
    wchar_t buf[32];
    swprintf(buf, arraysize(buf), L"Failed with %d: ", status.os_error());
    text.assign(buf);
  }

  text.append(URLForJob(job));
  text.append(L"\r\n");
  AppendText(text);
}

// IO thread:
void JobTracker::OnJobRedirect(URLRequestJob* job,
                               const GURL& location,
                               int status_code) {
  DCHECK(MessageLoop::current() != view_message_loop_);

  std::wstring text(L"- Redirect: ");
  text.append(URLForJob(job));
  text.append(L"\r\n  ");

  wchar_t buf[16];
  swprintf(buf, arraysize(buf), L"(%d) to: ", status_code);
  text.append(buf);

  text.append(StringForURL(location));
  text.append(L"\r\n");
  AppendText(text);
}

void JobTracker::OnBytesRead(URLRequestJob* job, int byte_count) {
}

// The singleton job tracker associated with the dialog.
JobTracker* tracker = NULL;

}  // namespace

// AboutNetworkDialog ----------------------------------------------------------

AboutNetworkDialog::AboutNetworkDialog() : tracking_(false) {
  SetupControls();
  tracker = new JobTracker(this);
  tracker->AddRef();
}

AboutNetworkDialog::~AboutNetworkDialog() {
  active_dialog = NULL;
  tracker->StopTracking();
  tracker->Release();
  tracker = NULL;
}

// static
void AboutNetworkDialog::RunDialog() {
  if (!active_dialog) {
    active_dialog = new AboutNetworkDialog;
    views::Window::CreateChromeWindow(NULL, gfx::Rect(), active_dialog)->Show();
  } else {
    // TOOD(brettw) it would be nice to focus the existing window.
  }
}

void AboutNetworkDialog::AppendText(const std::wstring& text) {
  text_field_->AppendText(text);
}

void AboutNetworkDialog::SetupControls() {
  views::GridLayout* layout = CreatePanelGridLayout(this);
  SetLayoutManager(layout);

  track_toggle_ = new views::TextButton(this, kStartTrackingLabel);
  show_button_ = new views::TextButton(this, kShowCurrentLabel);
  clear_button_ = new views::TextButton(this, kClearLabel);

  text_field_ = new views::TextField(static_cast<views::TextField::StyleFlags>(
                                     views::TextField::STYLE_MULTILINE));
  text_field_->SetReadOnly(true);

  // TODO(brettw): We may want to add this in the future. It can't be called
  // from here, though, since the hwnd for the field hasn't been created yet.
  //
  // This raises the maximum number of chars from 32K to some large maximum,
  // probably 2GB. 32K is not nearly enough for our use-case.
  //SendMessageW(text_field_->GetNativeComponent(), EM_SETLIMITTEXT, 0, 0);

  static const int first_column_set = 1;
  views::ColumnSet* column_set = layout->AddColumnSet(first_column_set);
  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
                        33.33f, views::GridLayout::FIXED, 0, 0);
  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
                        33.33f, views::GridLayout::FIXED, 0, 0);
  column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
                        33.33f, views::GridLayout::FIXED, 0, 0);

  static const int text_column_set = 2;
  column_set = layout->AddColumnSet(text_column_set);
  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
                        100.0f,
                        views::GridLayout::FIXED, 0, 0);

  layout->StartRow(0, first_column_set);
  layout->AddView(track_toggle_);
  layout->AddView(show_button_);
  layout->AddView(clear_button_);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
  layout->StartRow(1.0f, text_column_set);
  layout->AddView(text_field_);
}

gfx::Size AboutNetworkDialog::GetPreferredSize() {
  return gfx::Size(800, 400);
}

views::View* AboutNetworkDialog::GetContentsView() {
  return this;
}

int AboutNetworkDialog::GetDialogButtons() const {
  // Don't want OK or Cancel.
  return 0;
}

std::wstring AboutNetworkDialog::GetWindowTitle() const {
  return L"about:network";
}

bool AboutNetworkDialog::CanResize() const {
  return true;
}

void AboutNetworkDialog::ButtonPressed(views::Button* button) {
  if (button == track_toggle_) {
    if (tracking_) {
      track_toggle_->SetText(kStartTrackingLabel);
      tracking_ = false;
      tracker->StopTracking();
    } else {
      track_toggle_->SetText(kStopTrackingLabel);
      tracking_ = true;
      tracker->StartTracking();
    }
    track_toggle_->SchedulePaint();
  } else if (button == show_button_) {
    tracker->ReportStatus();
  } else if (button == clear_button_) {
    text_field_->SetText(std::wstring());
  }
}
<|endoftext|>


<commit_before>
#include <iostream>

#include "timer.h"
#include "matrix.h"

#if defined(AMATRIX_COMPARE_WITH_EIGEN)
#include "Eigen/Dense"
#endif

template <typename TMatrixType>
Timer::duration_type MeasureAccessTime(TMatrixType& A) {
    int repeat_number = 1000000;
    Timer timer;
    for (int i_repeat = 0; i_repeat < repeat_number; i_repeat++)
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                A(i, j) += i - j;
    return timer.elapsed().count();
}

template <typename TMatrixType>
Timer::duration_type MeasureAssignTime(TMatrixType& A) {
    int repeat_number = 1000000;
    TMatrixType B = A;
    Timer timer;
    for (int i_repeat = 0; i_repeat < repeat_number; i_repeat++) {
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                B(i, j) += i - j;
        A = B;
    }

    return timer.elapsed().count();
}

template <typename TMatrixType>
Timer::duration_type MeasureSumTime(TMatrixType& A, TMatrixType& B) {
    int repeat_number = 10000;
    TMatrixType C = A;
    TMatrixType D = A;
    Timer timer;
    for (int i_repeat = 0; i_repeat < repeat_number; i_repeat++) {
        C = A + B;
        B = C;
    }

    return timer.elapsed().count();
}

template <typename TMatrixType1, typename TMatrixType2>
Timer::duration_type MeasureMultTime(TMatrixType1& A, TMatrixType2& B) {
    int repeat_number = 1000;
    TMatrixType1 C = A;
    TMatrixType1 D = A;
    Timer timer;
    for (int i_repeat = 0; i_repeat < repeat_number; i_repeat++) {
        D = A;
        for (int i = 0; i < 1000; i++) {
            C = D * B;
            D = C;
        }
    }

    return timer.elapsed().count();
}

void CompareAccessTime() {
    AMatrix::Matrix<double, 3, 3> m_33(0.00);
    std::cout << "A[i,j] += i -j\t\t" << MeasureAccessTime(m_33) << std::endl;
}

void CompareAssignTime() {
    AMatrix::Matrix<double, 3, 3> m_33(0.00);
    std::cout << "A = B\t\t\t" << MeasureAssignTime(m_33) << std::endl;
}

int main() {
    std::cout << "operation\t\tAMatrix\t\tEigen\t\tUblas\t\tAtlas" << std::endl;
    CompareAccessTime();
    CompareAssignTime();
    return 0;
}
<commit_msg>Creating column comparison class<commit_after>
#include <iostream>

#include "timer.h"
#include "matrix.h"

#if defined(AMATRIX_COMPARE_WITH_EIGEN)
#include "Eigen/Dense"
#endif

template<typename TMatrixType, int NumberOfRows, int NumberOfColumns>
class ComparisonColumn{
	TMatrixType mA;
	void initialize(TMatrixType& TheMatrix){
		for(int i = 0 ; i < NumberOfRows ; i++)
			for(int j  = 0 ; j < NumberOfColumns ; j++)
				TheMatrix(i,j) = 0.00;
	}

public:

	ComparisonColumn(){
		initialize(mA);
	}

	Timer::duration_type MeasureAccessTime() {
		int repeat_number = 10000000;
		Timer timer;
		for (int i_repeat = 0; i_repeat < repeat_number; i_repeat++)
			for (int i = 0; i < 3; i++)
				for (int j = 0; j < 3; j++)
					mA(i, j) += i - j;
		return timer.elapsed().count();
	}

};

template <typename TMatrixType>
Timer::duration_type MeasureAssignTime(TMatrixType& A) {
    int repeat_number = 10000000;
    TMatrixType B = A;
    Timer timer;
    for (int i_repeat = 0; i_repeat < repeat_number; i_repeat++) {
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                B(i, j) += i - j;
        A = B;
    }

    return timer.elapsed().count();
}

template <typename TMatrixType>
Timer::duration_type MeasureSumTime(TMatrixType& A, TMatrixType& B) {
    int repeat_number = 10000;
    TMatrixType C = A;
    TMatrixType D = A;
    Timer timer;
    for (int i_repeat = 0; i_repeat < repeat_number; i_repeat++) {
        C = A + B;
        B = C;
    }

    return timer.elapsed().count();
}

template <typename TMatrixType1, typename TMatrixType2>
Timer::duration_type MeasureMultTime(TMatrixType1& A, TMatrixType2& B) {
    int repeat_number = 1000;
    TMatrixType1 C = A;
    TMatrixType1 D = A;
    Timer timer;
    for (int i_repeat = 0; i_repeat < repeat_number; i_repeat++) {
        D = A;
        for (int i = 0; i < 1000; i++) {
            C = D * B;
            D = C;
        }
    }

    return timer.elapsed().count();
}

void CompareAccessTime() {
    ComparisonColumn<AMatrix::Matrix<double, 3, 3>,3,3> a_matrix_column;
    ComparisonColumn<Eigen::Matrix<double, 3, 3>,3,3> eigen_column;
	std::cout << "A[i,j] += i -j\t\t" << a_matrix_column.MeasureAccessTime();
	std::cout << "\t\t" << eigen_column.MeasureAccessTime() << std::endl;
}

void CompareAssignTime() {
    AMatrix::Matrix<double, 3, 3> m_33(0.00);
    std::cout << "A = B\t\t\t" << MeasureAssignTime(m_33) << std::endl;
}

int main() {
    std::cout << "operation\t\tAMatrix\t\tEigen\t\tUblas\t\tAtlas" << std::endl;
    CompareAccessTime();
    CompareAssignTime();
    return 0;
}
<|endoftext|>


<commit_before>//Copyright (c) 2019 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include <gtest/gtest.h>

#include "../src/Application.h" //To set up a scene and load settings that the layer plan and merger need.
#include "../src/FanSpeedLayerTime.h" //Required to construct a layer plan. Doesn't influence our tests.
#include "../src/pathPlanning/GCodePath.h" //The paths that we're going to be merging.
#include "../src/LayerPlan.h" //Constructing plans that the mergers can merge lines in.
#include "../src/MergeInfillLines.h" //The class under test.
#include "../src/RetractionConfig.h" //Required to construct a layer plan. Doesn't influence our tests.
#include "../src/Slice.h" //To set up a scene and load settings that the layer plan and merger need.
#include "../src/settings/types/LayerIndex.h" //Required to construct a layer plan. Doesn't influence our tests.

namespace cura
{

class MergeInfillLinesTest : public testing::Test
{
public:
    //These settings don't matter for this test so they are all the same for every fixture.
    const size_t extruder_nr = 0;
    const LayerIndex layer_nr = 0;
    const bool is_initial_layer = false;
    const bool is_raft_layer = false;
    const coord_t layer_thickness = 100;
    const Point starting_position; //All plans start at 0,0.

    /*
     * A merger to test with.
     */
    ExtruderPlan* extruder_plan;
    MergeInfillLines* merger;

    /*
     * These fields are required for constructing layer plans and must be held
     * constant for as long as the lifetime of the plans. Construct them once
     * and store them in this fixture class.
     */
    const FanSpeedLayerTimeSettings fan_speed_layer_time;
    const RetractionConfig retraction_config;
    const GCodePathConfig skin_config;
    const GCodePathConfig travel_config;

    /*
     * A path of skin lines without any points.
     */
    GCodePath empty_skin;

    /*
     * A path of a single skin line.
     */
    GCodePath single_skin;

    /*
     * A path of multiple skin lines that together form a straight line.
     *
     * This path should not get merged together to a single line.
     */
    GCodePath lengthwise_skin;

    /*
     * Basic zigzag of skin lines.
     * There are 11 lines with travel moves in between them. The lines are 100
     * microns long and 400 microns wide. They should get merged to one long
     * line of 100 microns wide and 4400 microns long.
     */
    std::vector<GCodePath> zigzag;

    MergeInfillLinesTest()
     : starting_position(0, 0)
     , fan_speed_layer_time()
     , retraction_config()
     , skin_config(PrintFeatureType::Skin, 400, layer_thickness, 1, GCodePathConfig::SpeedDerivatives{50, 1000, 10})
     , travel_config(PrintFeatureType::MoveCombing, 0, layer_thickness, 0, GCodePathConfig::SpeedDerivatives{100, 1000, 10})
     , empty_skin(skin_config, "merge_infill_lines_mesh", SpaceFillType::None, 1.0, false)
     , single_skin(skin_config, "merge_infill_lines_mesh", SpaceFillType::Lines, 1.0, false)
     , lengthwise_skin(skin_config, "merge_infill_lines_mesh", SpaceFillType::Lines, 1.0, false)
    {
        single_skin.points.emplace_back(1000, 0);

        lengthwise_skin.points = {Point(1000, 0),
                                  Point(2000, 0),
                                  Point(3000, 0),
                                  Point(4000, 0)};

        //Create the zigzag.
        constexpr Ratio normal_flow = 1.0;
        constexpr bool no_spiralize = false;
        constexpr size_t num_lines = 10;
        //Creates a zig-zag line with extrusion moves when moving in the Y direction and travel moves in between:
        //  _   _   _
        // | |_| |_| |_|
        for(size_t i = 0; i < num_lines; i++)
        {
            zigzag.emplace_back(skin_config, "merge_infill_lines_mesh", SpaceFillType::Lines, normal_flow, no_spiralize);
            zigzag.back().points.emplace_back(400 * i, 100 * ((i + 1) % 2));
            zigzag.emplace_back(travel_config, "merge_infill_lines_mesh", SpaceFillType::None, normal_flow, no_spiralize);
            zigzag.back().points.emplace_back(400 * (i + 1), 100 * ((i + 1) % 2));
        }
        //End with an extrusion move, not a travel move.
        zigzag.emplace_back(skin_config, "merge_infill_lines_mesh", SpaceFillType::Lines, normal_flow, no_spiralize);
        zigzag.back().points.emplace_back(400 * num_lines, 100 * ((num_lines + 1) % 2));
    }

    void SetUp()
    {
        //Set up a scene so that we may request settings.
        Application::getInstance().current_slice = new Slice(1);
        Application::getInstance().current_slice->scene.extruders.emplace_back(0, nullptr);
        ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders.back();
        train.settings.add("machine_nozzle_size", "0.4");
        train.settings.add("meshfix_maximum_deviation", "0.1");

        extruder_plan = new ExtruderPlan(extruder_nr, layer_nr, is_initial_layer, is_raft_layer, layer_thickness, fan_speed_layer_time, retraction_config);
        merger = new MergeInfillLines(*extruder_plan);
    }

    void TearDown()
    {
        delete extruder_plan;
        delete merger;
        delete Application::getInstance().current_slice;
    }
};

TEST_F(MergeInfillLinesTest, CalcPathLengthEmpty)
{
    EXPECT_EQ(0, merger->calcPathLength(starting_position, empty_skin));
}

TEST_F(MergeInfillLinesTest, CalcPathLengthSingle)
{
    EXPECT_EQ(1000, merger->calcPathLength(starting_position, single_skin));
}

TEST_F(MergeInfillLinesTest, CalcPathLengthMultiple)
{
    EXPECT_EQ(4000, merger->calcPathLength(starting_position, lengthwise_skin));
}

/*
 * Tries merging an empty set of paths together.
 *
 * This changes nothing in the paths, since there is nothing to change.
 */
TEST_F(MergeInfillLinesTest, MergeEmpty)
{
    std::vector<GCodePath> paths; //Empty. No paths to merge.

    const bool result = merger->mergeInfillLines(paths, starting_position);

    EXPECT_FALSE(result) << "There are no lines to merge.";
    EXPECT_EQ(paths.size(), 0) << "The number of paths should still be zero.";
}

/*
 * Tries merging a single path of a single line.
 *
 * This changes nothing in the paths, since the line cannot be merged with
 * anything else.
 */
TEST_F(MergeInfillLinesTest, MergeSingle)
{
    std::vector<GCodePath> paths;
    paths.push_back(single_skin);

    const bool result = merger->mergeInfillLines(paths, starting_position);

    EXPECT_FALSE(result) << "There is only one line, so it can't be merged with other lines.";
    ASSERT_EQ(paths.size(), 1) << "The path should not get removed.";
    EXPECT_EQ(paths[0].points.size(), 1) << "The path should not be modified.";
}

/*
 * Tries merging a single path that consists of multiple vertices in a straight
 * line.
 *
 * This should not change anything in the paths, since the lines are in a single
 * path without travel moves in between. It's just drawing a curve, and that
 * curve should not get modified.
 *
 * This is basically the case that went wrong with the "Weird Fat Infill" bug
 * (CURA-5776).
 */
TEST_F(MergeInfillLinesTest, MergeLenthwise)
{
    std::vector<GCodePath> paths;
    paths.push_back(lengthwise_skin);

    const bool result = merger->mergeInfillLines(paths, starting_position);

    EXPECT_FALSE(result) << "Patterns like Gyroid infill with many (almost) lengthwise lines should not get merged, even if those lines are short.";
    ASSERT_EQ(paths.size(), 1) << "The path should not get removed or split.";
    EXPECT_EQ(paths[0].points.size(), 4) << "The path should not be modified.";
}

/*
 * Tries merging a bunch of parallel lines with travel moves in between.
 *
 * This is the basic use case for merging infill lines.
 */
TEST_F(MergeInfillLinesTest, MergeParallel)
{
    const bool result = merger->mergeInfillLines(zigzag, starting_position);

    EXPECT_TRUE(result) << "The simple zig-zag pattern should get merged fine.";
    EXPECT_LE(zigzag.size(), 5); //Some lenience. Ideally it'd be one.
}

/*
 * Tests if the total extruded volume is the same as the original lines.
 */
TEST_F(MergeInfillLinesTest, DISABLED_ExtrudedVolume)
{
    coord_t original_volume = 0;
    Point position = starting_position;
    for(const GCodePath& path : zigzag)
    {
        for(const Point& point : path.points)
        {
            const coord_t length = vSize(point - position);
            original_volume += length * (path.getExtrusionMM3perMM() * 1000000);
            position = point;
        }
    }

    merger->mergeInfillLines(zigzag, starting_position);
    /* If it fails to merge, other tests fail. This test depends on that, but we
    don't necessarily want it to fail as a false negative if merging in general
    fails, because we don't want to think that the volume is wrong then. So we
    don't check the outcome of the merging itself, just the volume. */

    coord_t new_volume = 0;
    position = starting_position;
    for(const GCodePath& path : zigzag)
    {
        for(const Point& point : path.points)
        {
            const coord_t length = vSize(point - position);
            new_volume += length * (path.getExtrusionMM3perMM() * 1000000);
            position = point;
        }
    }

    EXPECT_EQ(original_volume, new_volume);
}

} //namespace cura<commit_msg>Add parameterised integration test on merging thin walls<commit_after>//Copyright (c) 2019 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include <gtest/gtest.h>

#include "../src/Application.h" //To set up a scene and load settings that the layer plan and merger need.
#include "../src/FanSpeedLayerTime.h" //Required to construct a layer plan. Doesn't influence our tests.
#include "../src/pathPlanning/GCodePath.h" //The paths that we're going to be merging.
#include "../src/LayerPlan.h" //Constructing plans that the mergers can merge lines in.
#include "../src/MergeInfillLines.h" //The class under test.
#include "../src/RetractionConfig.h" //Required to construct a layer plan. Doesn't influence our tests.
#include "../src/Slice.h" //To set up a scene and load settings that the layer plan and merger need.
#include "../src/settings/types/LayerIndex.h" //Required to construct a layer plan. Doesn't influence our tests.

namespace cura
{

class MergeInfillLinesTest : public testing::Test
{
public:
    //These settings don't matter for this test so they are all the same for every fixture.
    const size_t extruder_nr = 0;
    const LayerIndex layer_nr = 0;
    const bool is_initial_layer = false;
    const bool is_raft_layer = false;
    const coord_t layer_thickness = 100;
    const Point starting_position; //All plans start at 0,0.

    /*
     * A merger to test with.
     */
    ExtruderPlan* extruder_plan;
    MergeInfillLines* merger;

    /*
     * These fields are required for constructing layer plans and must be held
     * constant for as long as the lifetime of the plans. Construct them once
     * and store them in this fixture class.
     */
    const FanSpeedLayerTimeSettings fan_speed_layer_time;
    const RetractionConfig retraction_config;
    const GCodePathConfig skin_config;
    const GCodePathConfig travel_config;

    /*
     * A path of skin lines without any points.
     */
    GCodePath empty_skin;

    /*
     * A path of a single skin line.
     */
    GCodePath single_skin;

    /*
     * A path of multiple skin lines that together form a straight line.
     *
     * This path should not get merged together to a single line.
     */
    GCodePath lengthwise_skin;

    /*
     * Basic zigzag of skin lines.
     * There are 11 lines with travel moves in between them. The lines are 100
     * microns long and 400 microns wide. They should get merged to one long
     * line of 100 microns wide and 4400 microns long.
     */
    std::vector<GCodePath> zigzag;

    MergeInfillLinesTest()
     : starting_position(0, 0)
     , fan_speed_layer_time()
     , retraction_config()
     , skin_config(PrintFeatureType::Skin, 400, layer_thickness, 1, GCodePathConfig::SpeedDerivatives{50, 1000, 10})
     , travel_config(PrintFeatureType::MoveCombing, 0, layer_thickness, 0, GCodePathConfig::SpeedDerivatives{100, 1000, 10})
     , empty_skin(skin_config, "merge_infill_lines_mesh", SpaceFillType::None, 1.0, false)
     , single_skin(skin_config, "merge_infill_lines_mesh", SpaceFillType::Lines, 1.0, false)
     , lengthwise_skin(skin_config, "merge_infill_lines_mesh", SpaceFillType::Lines, 1.0, false)
    {
        single_skin.points.emplace_back(1000, 0);

        lengthwise_skin.points = {Point(1000, 0),
                                  Point(2000, 0),
                                  Point(3000, 0),
                                  Point(4000, 0)};

        //Create the zigzag.
        constexpr Ratio normal_flow = 1.0;
        constexpr bool no_spiralize = false;
        constexpr size_t num_lines = 10;
        //Creates a zig-zag line with extrusion moves when moving in the Y direction and travel moves in between:
        //  _   _   _
        // | |_| |_| |_|
        for(size_t i = 0; i < num_lines; i++)
        {
            zigzag.emplace_back(skin_config, "merge_infill_lines_mesh", SpaceFillType::Lines, normal_flow, no_spiralize);
            zigzag.back().points.emplace_back(400 * i, 100 * ((i + 1) % 2));
            zigzag.emplace_back(travel_config, "merge_infill_lines_mesh", SpaceFillType::None, normal_flow, no_spiralize);
            zigzag.back().points.emplace_back(400 * (i + 1), 100 * ((i + 1) % 2));
        }
        //End with an extrusion move, not a travel move.
        zigzag.emplace_back(skin_config, "merge_infill_lines_mesh", SpaceFillType::Lines, normal_flow, no_spiralize);
        zigzag.back().points.emplace_back(400 * num_lines, 100 * ((num_lines + 1) % 2));
    }

    void SetUp()
    {
        //Set up a scene so that we may request settings.
        Application::getInstance().current_slice = new Slice(1);
        Application::getInstance().current_slice->scene.extruders.emplace_back(0, nullptr);
        ExtruderTrain& train = Application::getInstance().current_slice->scene.extruders.back();
        train.settings.add("machine_nozzle_size", "0.4");
        train.settings.add("meshfix_maximum_deviation", "0.1");

        extruder_plan = new ExtruderPlan(extruder_nr, layer_nr, is_initial_layer, is_raft_layer, layer_thickness, fan_speed_layer_time, retraction_config);
        merger = new MergeInfillLines(*extruder_plan);
    }

    void TearDown()
    {
        delete extruder_plan;
        delete merger;
        delete Application::getInstance().current_slice;
    }
};

TEST_F(MergeInfillLinesTest, CalcPathLengthEmpty)
{
    EXPECT_EQ(0, merger->calcPathLength(starting_position, empty_skin));
}

TEST_F(MergeInfillLinesTest, CalcPathLengthSingle)
{
    EXPECT_EQ(1000, merger->calcPathLength(starting_position, single_skin));
}

TEST_F(MergeInfillLinesTest, CalcPathLengthMultiple)
{
    EXPECT_EQ(4000, merger->calcPathLength(starting_position, lengthwise_skin));
}

/*
 * Tries merging an empty set of paths together.
 *
 * This changes nothing in the paths, since there is nothing to change.
 */
TEST_F(MergeInfillLinesTest, MergeEmpty)
{
    std::vector<GCodePath> paths; //Empty. No paths to merge.

    const bool result = merger->mergeInfillLines(paths, starting_position);

    EXPECT_FALSE(result) << "There are no lines to merge.";
    EXPECT_EQ(paths.size(), 0) << "The number of paths should still be zero.";
}

/*
 * Tries merging a single path of a single line.
 *
 * This changes nothing in the paths, since the line cannot be merged with
 * anything else.
 */
TEST_F(MergeInfillLinesTest, MergeSingle)
{
    std::vector<GCodePath> paths;
    paths.push_back(single_skin);

    const bool result = merger->mergeInfillLines(paths, starting_position);

    EXPECT_FALSE(result) << "There is only one line, so it can't be merged with other lines.";
    ASSERT_EQ(paths.size(), 1) << "The path should not get removed.";
    EXPECT_EQ(paths[0].points.size(), 1) << "The path should not be modified.";
}

/*
 * Tries merging a single path that consists of multiple vertices in a straight
 * line.
 *
 * This should not change anything in the paths, since the lines are in a single
 * path without travel moves in between. It's just drawing a curve, and that
 * curve should not get modified.
 *
 * This is basically the case that went wrong with the "Weird Fat Infill" bug
 * (CURA-5776).
 */
TEST_F(MergeInfillLinesTest, MergeLenthwise)
{
    std::vector<GCodePath> paths;
    paths.push_back(lengthwise_skin);

    const bool result = merger->mergeInfillLines(paths, starting_position);

    EXPECT_FALSE(result) << "Patterns like Gyroid infill with many (almost) lengthwise lines should not get merged, even if those lines are short.";
    ASSERT_EQ(paths.size(), 1) << "The path should not get removed or split.";
    EXPECT_EQ(paths[0].points.size(), 4) << "The path should not be modified.";
}

/*
 * Tries merging a bunch of parallel lines with travel moves in between.
 *
 * This is the basic use case for merging infill lines.
 */
TEST_F(MergeInfillLinesTest, MergeParallel)
{
    const bool result = merger->mergeInfillLines(zigzag, starting_position);

    EXPECT_TRUE(result) << "The simple zig-zag pattern should get merged fine.";
    EXPECT_LE(zigzag.size(), 5); //Some lenience. Ideally it'd be one.
}

/*
 * Tests if the total extruded volume is the same as the original lines.
 */
TEST_F(MergeInfillLinesTest, DISABLED_ExtrudedVolume)
{
    coord_t original_volume = 0;
    Point position = starting_position;
    for(const GCodePath& path : zigzag)
    {
        for(const Point& point : path.points)
        {
            const coord_t length = vSize(point - position);
            original_volume += length * (path.getExtrusionMM3perMM() * 1000000);
            position = point;
        }
    }

    merger->mergeInfillLines(zigzag, starting_position);
    /* If it fails to merge, other tests fail. This test depends on that, but we
    don't necessarily want it to fail as a false negative if merging in general
    fails, because we don't want to think that the volume is wrong then. So we
    don't check the outcome of the merging itself, just the volume. */

    coord_t new_volume = 0;
    position = starting_position;
    for(const GCodePath& path : zigzag)
    {
        for(const Point& point : path.points)
        {
            const coord_t length = vSize(point - position);
            new_volume += length * (path.getExtrusionMM3perMM() * 1000000);
            position = point;
        }
    }

    EXPECT_EQ(original_volume, new_volume);
}

/*
 * Parameterised test for merging infill lines inside thin walls.
 *
 * The thin walls are filled with lines of various rotations. This is the float
 * parameter.
 */
class MergeInfillLinesThinWallsTest : public MergeInfillLinesTest, public testing::WithParamInterface<double>
{
public:
    const coord_t wall_thickness = 200; //0.2mm wall.
};

TEST_P(MergeInfillLinesThinWallsTest, DISABLED_MergeThinWalls)
{
    const AngleRadians rotation = AngleRadians(GetParam()); //Converts degrees to radians!
    constexpr Ratio normal_flow = 1.0;
    constexpr bool no_spiralize = false;
    constexpr size_t num_lines = 10;

    //Construct parallel lines under a certain rotation. It looks like this: /////
    //The perpendicular distance between the lines will be exactly one line width.
    //The distance between the adjacent endpoints of the lines will be greater for steeper angles.
    std::vector<GCodePath> paths;
    const coord_t line_shift = wall_thickness / std::cos(rotation); //How far the top of the line is shifted from the bottom.
    const coord_t line_horizontal_spacing = skin_config.getLineWidth() / std::sin(rotation); //Horizontal spacing between starting vertices.
    for(size_t i = 0; i < num_lines; i++)
    {
        paths.emplace_back(skin_config, "merge_infill_lines_mesh", SpaceFillType::Lines, normal_flow, no_spiralize);
        paths.back().points.emplace_back(line_horizontal_spacing * i + line_shift * ((i + 1) % 2), wall_thickness * ((i + 1) % 2));
        paths.emplace_back(travel_config, "merge_infill_lines_mesh", SpaceFillType::None, normal_flow, no_spiralize);
        paths.back().points.emplace_back(line_horizontal_spacing * (i + 1) + line_shift * ((i + 1) % 2), wall_thickness * ((i + 1) % 2));
    }
    paths.emplace_back(skin_config, "merge_infill_lines_mesh", SpaceFillType::Lines, normal_flow, no_spiralize);
    paths.back().points.emplace_back(line_horizontal_spacing * num_lines + line_shift * ((num_lines + 1) % 2), wall_thickness * ((num_lines + 1) % 2));

    const bool merged = merger->mergeInfillLines(paths, starting_position);

    EXPECT_TRUE(merged) << "These are the test cases where line segments should get merged.";
    EXPECT_LE(paths.size(), 5);
}

INSTANTIATE_TEST_SUITE_P(MergeThinWallsTest, MergeInfillLinesThinWallsTest, testing::Range(-45.0, 45.0, 5.0));

} //namespace cura<|endoftext|>