<commit_before>/*****************************************************************************\
 *                        ANALYSIS PERFORMANCE TOOLS                         *
 *                                  wxparaver                                *
 *              Paraver Trace Visualization and Analysis Tool                *
 *****************************************************************************
 *     ___     This library is free software; you can redistribute it and/or *
 *    /  __         modify it under the terms of the GNU LGPL as published   *
 *   /  /  _____    by the Free Software Foundation; either version 2.1      *
 *  /  /  /     \   of the License, or (at your option) any later version.   *
 * (  (  ( B S C )                                                           *
 *  \  \  \_____/   This library is distributed in hope that it will be      *
 *   \  \__         useful but WITHOUT ANY WARRANTY; without even the        *
 *    \___          implied warranty of MERCHANTABILITY or FITNESS FOR A     *
 *                  PARTICULAR PURPOSE. See the GNU LGPL for more details.   *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public License  *
 * along with this library; if not, write to the Free Software Foundation,   *
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA          *
 * The GNU LEsser General Public License is contained in the file COPYING.   *
 *                                 ---------                                 *
 *   Barcelona Supercomputing Center - Centro Nacional de Supercomputacion   *
\*****************************************************************************/

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *\
 | @file: $HeadURL$
 | @last_commit: $Date$
 | @version:     $Revision$
\* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

////@begin includes
////@end includes
#include <string>

#include "helpcontents.h"
//#include "wx/html/htmlfilt.h"

#include "paravermain.h"
////@begin XPM images
////@end XPM images

/*!
 * HelpContents type definition
 */

IMPLEMENT_DYNAMIC_CLASS( HelpContents, wxDialog )

/*!
 * HelpContents event table definition
 */

BEGIN_EVENT_TABLE( HelpContents, wxDialog )

////@begin HelpContents event table entries
  EVT_HTML_LINK_CLICKED( ID_HTMLWINDOW, HelpContents::OnHtmlwindowLinkClicked )

  EVT_BUTTON( ID_BUTTON_INDEX, HelpContents::OnButtonIndexClick )

  EVT_BUTTON( ID_BUTTON_CLOSE, HelpContents::OnButtonCloseClick )

////@end HelpContents event table entries

END_EVENT_TABLE()

/*!
 * HelpContents constructors
 */

HelpContents::HelpContents()
{
  Init();
}

HelpContents::HelpContents( wxWindow* parent,
                            wxWindowID id,
                            const wxString& caption,
                            const wxPoint& pos,
                            const wxSize& size,
                            long style )
{
  Init();
  Create(parent, id, caption, pos, size, style);
}

/*!
 * HelpContents creator
 */

bool HelpContents::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
////@begin HelpContents creation
  SetExtraStyle(wxWS_EX_BLOCK_EVENTS);
  wxDialog::Create( parent, id, caption, pos, size, style );

  CreateControls();
  if (GetSizer())
  {
    GetSizer()->SetSizeHints(this);
  }
  Centre();
////@end HelpContents creation
  return true;
}

/*!
 * HelpContents destructor
 */

HelpContents::~HelpContents()
{
}

/*!
 * Member initialisation
 */
void HelpContents::Init()
{
  currentTutorialDir = _("");
}

const wxString HelpContents::getHtmlIndex( const wxString& path )
{
  wxString htmlIndex;
  wxFileName index( path + wxFileName::GetPathSeparator() + _("index.html") );

  if ( index.FileExists() )
  {
    htmlIndex = index.GetFullPath();
  }

  return htmlIndex;
}

const wxString HelpContents::getTitle( int numTutorial, const wxString& path )
{
  wxString tutorialTitle;

  wxHtmlWindow auxHtml( this );
  auxHtml.LoadPage( path  + wxFileName::GetPathSeparator() + _("index.html") );
  tutorialTitle = auxHtml.GetOpenedPageTitle();
  if ( tutorialTitle.empty() || tutorialTitle == _("index.html") ) // never is empty !?!
  {
    string auxStrTitleFileName( ( path +
                                   wxFileName::GetPathSeparator() +
                                   _("tutorial_title") ).mb_str() );
    string auxLine;

    ifstream titleFile;
    titleFile.open( auxStrTitleFileName.c_str() );
    if ( titleFile.good() )
    {
      std::getline( titleFile, auxLine );

      if ( auxLine.size() > 0 )
      {
        tutorialTitle = wxString::FromAscii( auxLine.c_str() );
      }
      else
      {
        tutorialTitle = _("Tutorial ");
        tutorialTitle << numTutorial;
      }
    }
    else
    {
      tutorialTitle = _("Tutorial ");
      tutorialTitle << numTutorial;
    }

    titleFile.close();
  }

  return tutorialTitle;
}

void HelpContents::appendTutorial( const wxString& title,
                                   const wxString& path,
                                   wxString& htmlDoc )
{
  htmlDoc += _("

") + title + _("

");
}

void HelpContents::htmlMessage( wxString& htmlDoc )
{
  htmlDoc += _("

No tutorial found!

");
/*
  htmlDoc += _("
Before going on, please have in mind that:
");
  htmlDoc += _("
");
  htmlDoc += _("
A single tutorials root directory must contain all ");
  htmlDoc += _("the tutorials directories that you want visible for wxparaver.
");
  htmlDoc += _("
In their own directories, every tutorial has a first page called <TT>index.html</TT>, and also related content (like traces, cfgs, etc.).
");
  htmlDoc += _("
You don't need to write any global tutorials page; we walk through the included tutorials ");
  htmlDoc += _("in the given root directory and build it for you.
");
  htmlDoc += _("
The tutorial title showed in this automatically built main index is read from:
");
  htmlDoc += _("
");
  htmlDoc += _("
The <TT>TITLE</TT> tag in the<TT>index.html</TT> file.
");
  htmlDoc += _("
If this tag is missing or empty, from a single line file named <TT>tutorial_title</TT>");
  htmlDoc += _(", also local to this tutorial.
");
  htmlDoc += _("
If no <TT>tutorial_title</TT> file is found, we give a numbered 'Tutorial #'.
");
  htmlDoc += _("
");
  htmlDoc += _("
");
*/
  htmlDoc += _("
Please check that root directory to tutorials is properly defined:
");
  htmlDoc += _("
");
  htmlDoc += _("
Open Preferences Window.
");
  htmlDoc += _("
Select Global tab.
");
  htmlDoc += _("
In the Default directories box, change the Tutorials root directory");
  htmlDoc += _("
The tutorials list will be immediately rebuilt after you save your ");
  htmlDoc += _("new settings clicking the Ok button in the Preferences Window.
");
  htmlDoc += _("
");
  htmlDoc += _("
If you still get this help, check these steps again, or please contact us at paraver@bsc.es.
");
}

void HelpContents::buildIndex()
{
  // get tutorials directory
  string auxStrPath = paraverMain::myParaverMain->GetParaverConfig()->getGlobalTutorialsPath();
  wxString auxPath = wxString::FromAscii( auxStrPath.c_str() );
  wxFileName tutorialsGlobalPath( auxPath );

  // open html index
  wxString tutorialsHtmlIndex, tutorialsList;
  tutorialsHtmlIndex += _("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">");
  tutorialsHtmlIndex += _("<TITLE>Tutorials</TITLE>");

  // look for tutorials directories, and for index.html inside them
  if ( wxDirExists( auxPath ) )
  {
    tutorialsList += _("
");
    int numTutorials = 0;

    wxString currentDir = wxFindFirstFile(
            tutorialsGlobalPath.GetLongPath() + wxFileName::GetPathSeparator() + _("*"),
            wxDIR );
    while( !currentDir.empty() )
    {
      wxString currentTutorialHtmlIndex( getHtmlIndex( currentDir ) );

      if ( currentTutorialHtmlIndex != _("")  )
      {
        // index.html found! => we consider this is a tutorial
        numTutorials++;

        appendTutorial( getTitle( numTutorials, currentDir ), currentTutorialHtmlIndex, tutorialsList );
      }

      currentDir = wxFindNextFile();
    }

    tutorialsList += _("
");

    if ( numTutorials == 0 )
    {
      htmlMessage( tutorialsHtmlIndex );
    }
    else
    {
      tutorialsHtmlIndex += _("

Index

");
      tutorialsHtmlIndex += tutorialsList;
    }
  }
  else
  {
    htmlMessage( tutorialsHtmlIndex );
  }

  // close html index
  tutorialsHtmlIndex += _("");

  htmlWindow->SetPage( tutorialsHtmlIndex );
}

/*!
 * Control creation for HelpContents
 */
void HelpContents::CreateControls()
{
////@begin HelpContents content construction
  HelpContents* itemDialog1 = this;

  wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
  itemDialog1->SetSizer(itemBoxSizer2);

  htmlWindow = new wxHtmlWindow( itemDialog1, ID_HTMLWINDOW, wxDefaultPosition, wxSize(600, 400), wxHW_SCROLLBAR_AUTO|wxSUNKEN_BORDER|wxHSCROLL|wxVSCROLL );
  itemBoxSizer2->Add(htmlWindow, 1, wxGROW|wxALL, 5);

  wxBoxSizer* itemBoxSizer4 = new wxBoxSizer(wxHORIZONTAL);
  itemBoxSizer2->Add(itemBoxSizer4, 0, wxGROW|wxALL, 5);

  wxButton* itemButton5 = new wxButton( itemDialog1, ID_BUTTON_INDEX, _("Index"), wxDefaultPosition, wxDefaultSize, 0 );
  itemBoxSizer4->Add(itemButton5, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);

  itemBoxSizer4->Add(5, 5, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5);

  wxButton* itemButton7 = new wxButton( itemDialog1, ID_BUTTON_CLOSE, _("Close"), wxDefaultPosition, wxDefaultSize, 0 );
  itemBoxSizer4->Add(itemButton7, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);

////@end HelpContents content construction

  buildIndex();
}

/*!
 * Should we show tooltips?
 */

bool HelpContents::ShowToolTips()
{
  return true;
}

/*!
 * Get bitmap resources
 */

wxBitmap HelpContents::GetBitmapResource( const wxString& name )
{
  // Bitmap retrieval
////@begin HelpContents bitmap retrieval
  wxUnusedVar(name);
  return wxNullBitmap;
////@end HelpContents bitmap retrieval
}

/*!
 * Get icon resources
 */

wxIcon HelpContents::GetIconResource( const wxString& name )
{
  // Icon retrieval
////@begin HelpContents icon retrieval
  wxUnusedVar(name);
  return wxNullIcon;
////@end HelpContents icon retrieval
}

std::string HelpContents::getHrefFullPath( wxHtmlLinkEvent &event )
{
  string hrefFullPath = paraverMain::myParaverMain->GetParaverConfig()->getGlobalTutorialsPath();
  hrefFullPath += wxString( wxFileName::GetPathSeparator() ).mb_str();
  hrefFullPath += currentTutorialDir.mb_str();
  hrefFullPath += wxString( wxFileName::GetPathSeparator() ).mb_str();
  hrefFullPath += std::string( event.GetLinkInfo().GetHref().mb_str() );

  return hrefFullPath;
}

bool HelpContents::matchHrefExtension( wxHtmlLinkEvent &event, const wxString extension )
{
  return ( event.GetLinkInfo().GetHref().Right( extension.Len() ).Cmp( extension ) == 0 );
}

/*!
 * wxEVT_COMMAND_HTML_LINK_CLICKED event handler for ID_HTMLWINDOW
 */

void HelpContents::OnHtmlwindowLinkClicked( wxHtmlLinkEvent& event )
{
  if ( matchHrefExtension( event, _(".prv") ) || matchHrefExtension( event, _(".prv.gz")))
  {
    paraverMain::myParaverMain->DoLoadTrace( getHrefFullPath( event ) );
  }
  else if ( matchHrefExtension( event, _(".cfg")))
  {
    if ( paraverMain::myParaverMain->GetLoadedTraces().size() > 0 )
    {
      paraverMain::myParaverMain->DoLoadCFG( getHrefFullPath( event )  );
    }
    else
    {
      wxMessageDialog message( this, _("No trace loaded."), _( "Warning" ), wxOK );
      message.ShowModal();
    }
  }
  else if ( event.GetLinkInfo().GetHref().Cmp( _("init_preferences") ) == 0 )
  {
    paraverMain::myParaverMain->ShowPreferences();

    // we rebuild it anyway
    buildIndex();
  }
  else
  {
    // If current clicked link points to a tutorial index.html file, keep its
    //   tutorial directory name to allow relative references.
    // Idea to detect tutorial:
    //   /home/user/root-tutorials/ => dir depth tutorials = 3
    //   vs.
    //   /home/user/root-tutorials/tutorial1/index.html => dir depth current = 4
    //   /home/user/root-tutorials/tutorial1/html/.../anyotherpage.html => dir depth current > 4
    wxString anyTutorialPath =
            wxString::FromAscii( paraverMain::myParaverMain->GetParaverConfig()->getGlobalTutorialsPath().c_str() );
    if ( anyTutorialPath[ anyTutorialPath.Len() - 1 ] != wxString( wxFileName::GetPathSeparator() ))
    {
      // last separator needed to count properly
      anyTutorialPath += wxString( wxFileName::GetPathSeparator() );
    }

    wxFileName anyTutorialDir( anyTutorialPath );

    size_t dirsDepthTutorials = anyTutorialDir.GetDirCount();

    wxFileName currentLink( event.GetLinkInfo().GetHref() );
    size_t dirsDepthCurrentLink = currentLink.GetDirCount();

    if (( dirsDepthCurrentLink == dirsDepthTutorials + 1 ) &&
        ( currentLink.GetFullName().Cmp( _("index.html") ) == 0 ))
    {
      wxArrayString dirs = currentLink.GetDirs();
      currentTutorialDir = dirs[ dirsDepthCurrentLink - 1 ];
    }

    // and let the html window browse it.
    event.Skip();
  }
}

/*!
 * wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_BUTTON_CLOSE
 */

void HelpContents::OnButtonCloseClick( wxCommandEvent& event )
{
  Close();
}

/*!
 * wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_BUTTON_INDEX
 */

void HelpContents::OnButtonIndexClick( wxCommandEvent& event )
{
  buildIndex();
}

<commit_msg>*** empty log message ***<commit_after>/*****************************************************************************\
 *                        ANALYSIS PERFORMANCE TOOLS                         *
 *                                  wxparaver                                *
 *              Paraver Trace Visualization and Analysis Tool                *
 *****************************************************************************
 *     ___     This library is free software; you can redistribute it and/or *
 *    /  __         modify it under the terms of the GNU LGPL as published   *
 *   /  /  _____    by the Free Software Foundation; either version 2.1      *
 *  /  /  /     \   of the License, or (at your option) any later version.   *
 * (  (  ( B S C )                                                           *
 *  \  \  \_____/   This library is distributed in hope that it will be      *
 *   \  \__         useful but WITHOUT ANY WARRANTY; without even the        *
 *    \___          implied warranty of MERCHANTABILITY or FITNESS FOR A     *
 *                  PARTICULAR PURPOSE. See the GNU LGPL for more details.   *
 *                                                                           *
 * You should have received a copy of the GNU Lesser General Public License  *
 * along with this library; if not, write to the Free Software Foundation,   *
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA          *
 * The GNU LEsser General Public License is contained in the file COPYING.   *
 *                                 ---------                                 *
 *   Barcelona Supercomputing Center - Centro Nacional de Supercomputacion   *
\*****************************************************************************/

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- *\
 | @file: $HeadURL$
 | @last_commit: $Date$
 | @version:     $Revision$
\* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

////@begin includes
////@end includes
#include <string>

#include "helpcontents.h"
//#include "wx/html/htmlfilt.h"

#include "paravermain.h"

#include "trace.h"        // load xml
#include "traceoptions.h" // load xml

////@begin XPM images
////@end XPM images

/*!
 * HelpContents type definition
 */

IMPLEMENT_DYNAMIC_CLASS( HelpContents, wxDialog )

/*!
 * HelpContents event table definition
 */

BEGIN_EVENT_TABLE( HelpContents, wxDialog )

////@begin HelpContents event table entries
  EVT_HTML_LINK_CLICKED( ID_HTMLWINDOW, HelpContents::OnHtmlwindowLinkClicked )

  EVT_BUTTON( ID_BUTTON_INDEX, HelpContents::OnButtonIndexClick )

  EVT_BUTTON( ID_BUTTON_CLOSE, HelpContents::OnButtonCloseClick )

////@end HelpContents event table entries

END_EVENT_TABLE()

/*!
 * HelpContents constructors
 */

HelpContents::HelpContents()
{
  Init();
}

HelpContents::HelpContents( wxWindow* parent,
                            wxWindowID id,
                            const wxString& caption,
                            const wxPoint& pos,
                            const wxSize& size,
                            long style )
{
  Init();
  Create(parent, id, caption, pos, size, style);
}

/*!
 * HelpContents creator
 */

bool HelpContents::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
////@begin HelpContents creation
  SetExtraStyle(wxWS_EX_BLOCK_EVENTS);
  wxDialog::Create( parent, id, caption, pos, size, style );

  CreateControls();
  if (GetSizer())
  {
    GetSizer()->SetSizeHints(this);
  }
  Centre();
////@end HelpContents creation
  return true;
}

/*!
 * HelpContents destructor
 */

HelpContents::~HelpContents()
{
}

/*!
 * Member initialisation
 */
void HelpContents::Init()
{
  currentTutorialDir = _("");
}

const wxString HelpContents::getHtmlIndex( const wxString& path )
{
  wxString htmlIndex;
  wxFileName index( path + wxFileName::GetPathSeparator() + _("index.html") );

  if ( index.FileExists() )
  {
    htmlIndex = index.GetFullPath();
  }

  return htmlIndex;
}

const wxString HelpContents::getTitle( int numTutorial, const wxString& path )
{
  wxString tutorialTitle;

  wxHtmlWindow auxHtml( this );
  auxHtml.LoadPage( path  + wxFileName::GetPathSeparator() + _("index.html") );
  tutorialTitle = auxHtml.GetOpenedPageTitle();
  if ( tutorialTitle.empty() || tutorialTitle == _("index.html") ) // never is empty !?!
  {
    string auxStrTitleFileName( ( path +
                                   wxFileName::GetPathSeparator() +
                                   _("tutorial_title") ).mb_str() );
    string auxLine;

    ifstream titleFile;
    titleFile.open( auxStrTitleFileName.c_str() );
    if ( titleFile.good() )
    {
      std::getline( titleFile, auxLine );

      if ( auxLine.size() > 0 )
      {
        tutorialTitle = wxString::FromAscii( auxLine.c_str() );
      }
      else
      {
        tutorialTitle = _("Tutorial ");
        tutorialTitle << numTutorial;
      }
    }
    else
    {
      tutorialTitle = _("Tutorial ");
      tutorialTitle << numTutorial;
    }

    titleFile.close();
  }

  return tutorialTitle;
}

void HelpContents::appendTutorial( const wxString& title,
                                   const wxString& path,
                                   wxString& htmlDoc )
{
  htmlDoc += _("

") + title + _("

");
}

void HelpContents::htmlMessage( wxString& htmlDoc )
{
  htmlDoc += _("

No tutorial found!

");
/*
  htmlDoc += _("
Before going on, please have in mind that:
");
  htmlDoc += _("
");
  htmlDoc += _("
A single tutorials root directory must contain all ");
  htmlDoc += _("the tutorials directories that you want visible for wxparaver.
");
  htmlDoc += _("
In their own directories, every tutorial has a first page called <TT>index.html</TT>, and also related content (like traces, cfgs, etc.).
");
  htmlDoc += _("
You don't need to write any global tutorials page; we walk through the included tutorials ");
  htmlDoc += _("in the given root directory and build it for you.
");
  htmlDoc += _("
The tutorial title showed in this automatically built main index is read from:
");
  htmlDoc += _("
");
  htmlDoc += _("
The <TT>TITLE</TT> tag in the<TT>index.html</TT> file.
");
  htmlDoc += _("
If this tag is missing or empty, from a single line file named <TT>tutorial_title</TT>");
  htmlDoc += _(", also local to this tutorial.
");
  htmlDoc += _("
If no <TT>tutorial_title</TT> file is found, we give a numbered 'Tutorial #'.
");
  htmlDoc += _("
");
  htmlDoc += _("
");
*/
  htmlDoc += _("
Please check that root directory to tutorials is properly defined:
");
  htmlDoc += _("
");
  htmlDoc += _("
Open Preferences Window.
");
  htmlDoc += _("
Select Global tab.
");
  htmlDoc += _("
In the Default directories box, change the Tutorials root directory");
  htmlDoc += _("
The tutorials list will be immediately rebuilt after you save your ");
  htmlDoc += _("new settings clicking the Ok button in the Preferences Window.
");
  htmlDoc += _("
");
  htmlDoc += _("
If you still get this help, check these steps again, or please contact us at paraver@bsc.es.
");
}

void HelpContents::buildIndex()
{
  // get tutorials directory
  string auxStrPath = paraverMain::myParaverMain->GetParaverConfig()->getGlobalTutorialsPath();
  wxString auxPath = wxString::FromAscii( auxStrPath.c_str() );
  wxFileName tutorialsGlobalPath( auxPath );

  // open html index
  wxString tutorialsHtmlIndex, tutorialsList;
  tutorialsHtmlIndex += _("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">");
  tutorialsHtmlIndex += _("<TITLE>Tutorials</TITLE>");

  // look for tutorials directories, and for index.html inside them
  if ( wxDirExists( auxPath ) )
  {
    tutorialsList += _("
");
    int numTutorials = 0;

    wxString currentDir = wxFindFirstFile(
            tutorialsGlobalPath.GetLongPath() + wxFileName::GetPathSeparator() + _("*"),
            wxDIR );
    while( !currentDir.empty() )
    {
      wxString currentTutorialHtmlIndex( getHtmlIndex( currentDir ) );

      if ( currentTutorialHtmlIndex != _("")  )
      {
        // index.html found! => we consider this is a tutorial
        numTutorials++;

        appendTutorial( getTitle( numTutorials, currentDir ), currentTutorialHtmlIndex, tutorialsList );
      }

      currentDir = wxFindNextFile();
    }

    tutorialsList += _("
");

    if ( numTutorials == 0 )
    {
      htmlMessage( tutorialsHtmlIndex );
    }
    else
    {
      tutorialsHtmlIndex += _("

Index

");
      tutorialsHtmlIndex += tutorialsList;
    }
  }
  else
  {
    htmlMessage( tutorialsHtmlIndex );
  }

  // close html index
  tutorialsHtmlIndex += _("");

  htmlWindow->SetPage( tutorialsHtmlIndex );
}

/*!
 * Control creation for HelpContents
 */
void HelpContents::CreateControls()
{
////@begin HelpContents content construction
  HelpContents* itemDialog1 = this;

  wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
  itemDialog1->SetSizer(itemBoxSizer2);

  htmlWindow = new wxHtmlWindow( itemDialog1, ID_HTMLWINDOW, wxDefaultPosition, wxSize(600, 400), wxHW_SCROLLBAR_AUTO|wxSUNKEN_BORDER|wxHSCROLL|wxVSCROLL );
  itemBoxSizer2->Add(htmlWindow, 1, wxGROW|wxALL, 5);

  wxBoxSizer* itemBoxSizer4 = new wxBoxSizer(wxHORIZONTAL);
  itemBoxSizer2->Add(itemBoxSizer4, 0, wxGROW|wxALL, 5);

  wxButton* itemButton5 = new wxButton( itemDialog1, ID_BUTTON_INDEX, _("Index"), wxDefaultPosition, wxDefaultSize, 0 );
  itemBoxSizer4->Add(itemButton5, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);

  itemBoxSizer4->Add(5, 5, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5);

  wxButton* itemButton7 = new wxButton( itemDialog1, ID_BUTTON_CLOSE, _("Close"), wxDefaultPosition, wxDefaultSize, 0 );
  itemBoxSizer4->Add(itemButton7, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);

////@end HelpContents content construction

  buildIndex();
}

/*!
 * Should we show tooltips?
 */

bool HelpContents::ShowToolTips()
{
  return true;
}

/*!
 * Get bitmap resources
 */

wxBitmap HelpContents::GetBitmapResource( const wxString& name )
{
  // Bitmap retrieval
////@begin HelpContents bitmap retrieval
  wxUnusedVar(name);
  return wxNullBitmap;
////@end HelpContents bitmap retrieval
}

/*!
 * Get icon resources
 */

wxIcon HelpContents::GetIconResource( const wxString& name )
{
  // Icon retrieval
////@begin HelpContents icon retrieval
  wxUnusedVar(name);
  return wxNullIcon;
////@end HelpContents icon retrieval
}

std::string HelpContents::getCurrentTutorialFullPath()
{
  string fullPath = paraverMain::myParaverMain->GetParaverConfig()->getGlobalTutorialsPath();
  fullPath += wxString( wxFileName::GetPathSeparator() ).mb_str();
  fullPath += currentTutorialDir.mb_str();
  fullPath += wxString( wxFileName::GetPathSeparator() ).mb_str();

  return fullPath;
}

std::string HelpContents::getHrefFullPath( wxHtmlLinkEvent &event )
{
  string hrefFullPath = getCurrentTutorialFullPath();
  hrefFullPath += std::string( event.GetLinkInfo().GetHref().mb_str() );

  return hrefFullPath;
}

bool HelpContents::matchHrefExtension( wxHtmlLinkEvent &event, const wxString extension )
{
  return ( event.GetLinkInfo().GetHref().Right( extension.Len() ).Cmp( extension ) == 0 );
}

/*!
 * wxEVT_COMMAND_HTML_LINK_CLICKED event handler for ID_HTMLWINDOW
 */

void HelpContents::OnHtmlwindowLinkClicked( wxHtmlLinkEvent& event )
{
  if ( matchHrefExtension( event, _(".prv") ) || matchHrefExtension( event, _(".prv.gz")))
  {
    paraverMain::myParaverMain->DoLoadTrace( getHrefFullPath( event ) );
  }
  else if ( matchHrefExtension( event, _(".cfg")))
  {
    if ( paraverMain::myParaverMain->GetLoadedTraces().size() > 0 )
    {
      paraverMain::myParaverMain->DoLoadCFG( getHrefFullPath( event )  );
    }
    else
    {
      wxMessageDialog message( this, _("No trace loaded."), _( "Warning" ), wxOK );
      message.ShowModal();
    }
  }
  else if ( matchHrefExtension( event, _(".xml")))
  {
    string traceName = getCurrentTutorialFullPath();
    bool loadTrace = true;
    string strXmlFile = getHrefFullPath( event );

    paraverMain::myParaverMain->ShowCutTraceWindow( traceName, loadTrace, strXmlFile );
  }
  else if ( event.GetLinkInfo().GetHref().Cmp( _("init_preferences") ) == 0 )
  {
    paraverMain::myParaverMain->ShowPreferences();

    // we rebuild it anyway
    buildIndex();
  }
  else
  {
    // If current clicked link points to a tutorial index.html file, keep its
    //   tutorial directory name to allow relative references.
    // Idea to detect tutorial:
    //   /home/user/root-tutorials/ => dir depth tutorials = 3
    //   vs.
    //   /home/user/root-tutorials/tutorial1/index.html => dir depth current = 4
    //   /home/user/root-tutorials/tutorial1/html/.../anyotherpage.html => dir depth current > 4
    wxString anyTutorialPath =
            wxString::FromAscii( paraverMain::myParaverMain->GetParaverConfig()->getGlobalTutorialsPath().c_str() );
    if ( anyTutorialPath[ anyTutorialPath.Len() - 1 ] != wxString( wxFileName::GetPathSeparator() ))
    {
      // last separator needed to count properly
      anyTutorialPath += wxString( wxFileName::GetPathSeparator() );
    }

    wxFileName anyTutorialDir( anyTutorialPath );

    size_t dirsDepthTutorials = anyTutorialDir.GetDirCount();

    wxFileName currentLink( event.GetLinkInfo().GetHref() );
    size_t dirsDepthCurrentLink = currentLink.GetDirCount();

    if (( dirsDepthCurrentLink == dirsDepthTutorials + 1 ) &&
        ( currentLink.GetFullName().Cmp( _("index.html") ) == 0 ))
    {
      wxArrayString dirs = currentLink.GetDirs();
      currentTutorialDir = dirs[ dirsDepthCurrentLink - 1 ];
    }

    // and let the html window browse it.
    event.Skip();
  }
}

/*!
 * wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_BUTTON_CLOSE
 */

void HelpContents::OnButtonCloseClick( wxCommandEvent& event )
{
  Close();
}

/*!
 * wxEVT_COMMAND_BUTTON_CLICKED event handler for ID_BUTTON_INDEX
 */

void HelpContents::OnButtonIndexClick( wxCommandEvent& event )
{
  buildIndex();
}

<|endoftext|>


<commit_before>/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

// See docs in ../ops/string_ops.cc.

#include <string>

#include "tensorflow/core/framework/kernel_def_builder.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/lib/strings/stringprintf.h"

namespace tensorflow {

class AsStringOp : public OpKernel {
 public:
  using OpKernel::OpKernel;

  explicit AsStringOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
    int32 precision;
    bool scientific;
    bool shortest;
    int32 width;
    string fill_string;
    DataType dtype;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("T", &dtype));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("precision", &precision));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("scientific", &scientific));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("shortest", &shortest));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("width", &width));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("fill", &fill_string));
    switch (dtype) {
      case DT_FLOAT:
      case DT_DOUBLE:
      case DT_COMPLEX64:
        break;
      default:
        OP_REQUIRES(ctx, !(scientific || shortest),
                    errors::InvalidArgument("scientific and shortest format "
                                            "not supported for datatype ",
                                            DataTypeString(dtype)));
        OP_REQUIRES(ctx, precision < 0,
                    errors::InvalidArgument("precision not supported "
                                            "for datatype ",
                                            DataTypeString(dtype)));
    }
    OP_REQUIRES(
        ctx, fill_string.size() <= 1,
        errors::InvalidArgument("Fill string must be one or fewer characters"));
    OP_REQUIRES(ctx, !(scientific && shortest),
                errors::InvalidArgument(
                    "Cannot select both scientific and shortest notation"));
    format_ = "%";
    if (width > -1) {
      strings::Appendf(&format_, "%s%d", fill_string.c_str(), width);
    }
    if (precision > -1) {
      strings::Appendf(&format_, ".%d", precision);
    }
    switch (dtype) {
      case DT_INT8:
      case DT_INT32:
        strings::Appendf(&format_, "d");
        break;
      case DT_INT64:
        strings::Appendf(&format_, "lld");
        break;
      case DT_FLOAT:
      case DT_DOUBLE:
      case DT_COMPLEX64:
        if (shortest) {
          strings::Appendf(&format_, "g");
        } else if (scientific) {
          strings::Appendf(&format_, "e");
        } else {
          strings::Appendf(&format_, "f");
        }
        break;
      case DT_BOOL:
        break;
      default:
        bool type_not_supported = true;
        OP_REQUIRES(ctx, !type_not_supported,
                    errors::InvalidArgument("Type not supported: ",
                                            DataTypeString(dtype)));
    }

    if (dtype == DT_COMPLEX64) {
      format_ = strings::Printf("(%s,%s)", format_.c_str(), format_.c_str());
    }
  }

  void Compute(OpKernelContext* context) override {
    const Tensor* input_tensor;
    OP_REQUIRES_OK(context, context->input("input", &input_tensor));
    const DataType& dtype = input_tensor->dtype();

    Tensor* output_tensor = nullptr;
    OP_REQUIRES_OK(context,
                   context->allocate_output("output", input_tensor->shape(),
                                            &output_tensor));
    auto output_flat = output_tensor->flat<string>();

#define ENCODE_TYPE(type, T, enc_str)                                     \
  case (type): {                                                          \
    const auto& input_flat = input_tensor->flat<T>();                     \
    for (int i = 0; i < input_flat.size(); ++i) {                         \
      output_flat(i) = strings::Printf((enc_str.c_str()), input_flat(i)); \
    }                                                                     \
  } break

    switch (dtype) {
      ENCODE_TYPE(DT_INT32, int32, format_);
      ENCODE_TYPE(DT_INT64, int64, format_);
      ENCODE_TYPE(DT_FLOAT, float, format_);
      ENCODE_TYPE(DT_DOUBLE, double, format_);
      ENCODE_TYPE(DT_INT8, int8, format_);
      case (DT_BOOL): {
        const auto& input_flat = input_tensor->flat<bool>();
        for (int i = 0; i < input_flat.size(); ++i) {
          output_flat(i) = (input_flat(i)) ? "true" : "false";
        }
      } break;
      case (DT_COMPLEX64): {
        const auto& input_flat = input_tensor->flat<complex64>();
        for (int i = 0; i < input_flat.size(); ++i) {
          output_flat(i) = strings::Printf(
              format_.c_str(), input_flat(i).real(), input_flat(i).imag());
        }
      } break;
      default:
        bool can_encode_type = false;
        OP_REQUIRES(context, can_encode_type,
                    errors::InvalidArgument("Cannot encode input of type ",
                                            DataTypeString(dtype)));
    }

#undef ENCODE_TYPE
  }

 private:
  string format_;
};

REGISTER_KERNEL_BUILDER(Name("AsString").Device(DEVICE_CPU), AsStringOp);

}  // namespace tensorflow
<commit_msg>Add int16 support for `tf.as_string`<commit_after>/* Copyright 2016 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

// See docs in ../ops/string_ops.cc.

#include <string>

#include "tensorflow/core/framework/kernel_def_builder.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/lib/strings/stringprintf.h"

namespace tensorflow {

class AsStringOp : public OpKernel {
 public:
  using OpKernel::OpKernel;

  explicit AsStringOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
    int32 precision;
    bool scientific;
    bool shortest;
    int32 width;
    string fill_string;
    DataType dtype;
    OP_REQUIRES_OK(ctx, ctx->GetAttr("T", &dtype));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("precision", &precision));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("scientific", &scientific));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("shortest", &shortest));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("width", &width));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("fill", &fill_string));
    switch (dtype) {
      case DT_FLOAT:
      case DT_DOUBLE:
      case DT_COMPLEX64:
        break;
      default:
        OP_REQUIRES(ctx, !(scientific || shortest),
                    errors::InvalidArgument("scientific and shortest format "
                                            "not supported for datatype ",
                                            DataTypeString(dtype)));
        OP_REQUIRES(ctx, precision < 0,
                    errors::InvalidArgument("precision not supported "
                                            "for datatype ",
                                            DataTypeString(dtype)));
    }
    OP_REQUIRES(
        ctx, fill_string.size() <= 1,
        errors::InvalidArgument("Fill string must be one or fewer characters"));
    OP_REQUIRES(ctx, !(scientific && shortest),
                errors::InvalidArgument(
                    "Cannot select both scientific and shortest notation"));
    format_ = "%";
    if (width > -1) {
      strings::Appendf(&format_, "%s%d", fill_string.c_str(), width);
    }
    if (precision > -1) {
      strings::Appendf(&format_, ".%d", precision);
    }
    switch (dtype) {
      case DT_INT8:
      case DT_INT16:
      case DT_INT32:
        strings::Appendf(&format_, "d");
        break;
      case DT_INT64:
        strings::Appendf(&format_, "lld");
        break;
      case DT_FLOAT:
      case DT_DOUBLE:
      case DT_COMPLEX64:
        if (shortest) {
          strings::Appendf(&format_, "g");
        } else if (scientific) {
          strings::Appendf(&format_, "e");
        } else {
          strings::Appendf(&format_, "f");
        }
        break;
      case DT_BOOL:
        break;
      default:
        bool type_not_supported = true;
        OP_REQUIRES(ctx, !type_not_supported,
                    errors::InvalidArgument("Type not supported: ",
                                            DataTypeString(dtype)));
    }

    if (dtype == DT_COMPLEX64) {
      format_ = strings::Printf("(%s,%s)", format_.c_str(), format_.c_str());
    }
  }

  void Compute(OpKernelContext* context) override {
    const Tensor* input_tensor;
    OP_REQUIRES_OK(context, context->input("input", &input_tensor));
    const DataType& dtype = input_tensor->dtype();

    Tensor* output_tensor = nullptr;
    OP_REQUIRES_OK(context,
                   context->allocate_output("output", input_tensor->shape(),
                                            &output_tensor));
    auto output_flat = output_tensor->flat<string>();

#define ENCODE_TYPE(type, T, enc_str)                                     \
  case (type): {                                                          \
    const auto& input_flat = input_tensor->flat<T>();                     \
    for (int i = 0; i < input_flat.size(); ++i) {                         \
      output_flat(i) = strings::Printf((enc_str.c_str()), input_flat(i)); \
    }                                                                     \
  } break

    switch (dtype) {
      ENCODE_TYPE(DT_INT32, int32, format_);
      ENCODE_TYPE(DT_INT64, int64, format_);
      ENCODE_TYPE(DT_FLOAT, float, format_);
      ENCODE_TYPE(DT_DOUBLE, double, format_);
      ENCODE_TYPE(DT_INT8, int8, format_);
      ENCODE_TYPE(DT_INT16, int16, format_);
      case (DT_BOOL): {
        const auto& input_flat = input_tensor->flat<bool>();
        for (int i = 0; i < input_flat.size(); ++i) {
          output_flat(i) = (input_flat(i)) ? "true" : "false";
        }
      } break;
      case (DT_COMPLEX64): {
        const auto& input_flat = input_tensor->flat<complex64>();
        for (int i = 0; i < input_flat.size(); ++i) {
          output_flat(i) = strings::Printf(
              format_.c_str(), input_flat(i).real(), input_flat(i).imag());
        }
      } break;
      default:
        bool can_encode_type = false;
        OP_REQUIRES(context, can_encode_type,
                    errors::InvalidArgument("Cannot encode input of type ",
                                            DataTypeString(dtype)));
    }

#undef ENCODE_TYPE
  }

 private:
  string format_;
};

REGISTER_KERNEL_BUILDER(Name("AsString").Device(DEVICE_CPU), AsStringOp);

}  // namespace tensorflow
<|endoftext|>


<commit_before>#include <fmt/format.h>
#include <ncurses.h>

#include "../../string.hpp"
#include "screens/index.hpp"
#include "screens/item_details.hpp"

namespace bookwyrm::tui::screen {

    void index::columns_t::operator=(std::vector<std::pair<std::string, column_t::width_w_t>> &&pairs)
    {
        int i = 0;
        for (auto &&pair : pairs) {
            columns_[i].width_w = std::get<1>(pair);
            columns_[i++].title = std::get<0>(pair);
        }
    }

    index::index(std::set<core::item> const &items)
        : base(default_padding_top, default_padding_bot, default_padding_left, default_padding_right), selected_item_(0),
          scroll_offset_(0), items_(items)
    {
        /*
         * These wanted widths works fine for now,
         * but we are still not utilizing the full 100%
         * of the width, which we should.
         *
         * TODO: Remedy this.
         */
        columns_ = {
            {"Title", .30},
            {"Year", 4},
            {"Series", .15},
            {"Authors", .20},
            {"Publisher", .15},
            {"Format", 6},
        };

        update_column_widths();
    }

    void index::paint()
    {
        erase();

        size_t x = 0;
        /* Start at x = 1 to align with column strings. */
        for (auto &column : columns_) {
            /* Can we fit the next header? */
            if (column.width > get_width() - x)
                break;

            x += print_header(column);
            print_column(column);
        }

        refresh();
    }

    std::string index::footer_info() const
    {
        if (plugin_count_ == 0) {
            return fmt::format("Search finished: I found {} items.", item_count());
        } else {
            return fmt::format(
                "Searching with {} running plugins... I have found {} items thus far.", plugin_count_, item_count());
        }
    }

    std::string index::controls_legacy() const { return "[j/k d/u G/g]Navigation [SPACE]Toggle select [l/->]Open details"; }

    int index::scrollpercent() const
    {
        if (item_count() <= menu_capacity())
            return scroll::not_applicable;

        return ratio(selected_item_, item_count());
    }

    void index::prepare(int plugin_count) { plugin_count_ = plugin_count; }

    bool index::is_marked(const size_t idx) const { return marked_items_.find(idx) != marked_items_.cend(); }

    size_t index::menu_capacity() const { return get_height() - 1; }

    bool index::menu_at_bot() const { return selected_item_ == (menu_capacity() - 1 + scroll_offset_); }

    bool index::menu_at_top() const { return selected_item_ == scroll_offset_; }

    void index::move(move_direction dir)
    {
        const bool at_first_item = selected_item_ == 0, at_last_item = selected_item_ == (item_count() - 1);

        switch (dir) {
        case up:
            if (at_first_item)
                return;
            if (menu_at_top())
                scroll_offset_--;
            selected_item_--;
            break;
        case down:
            if (at_last_item)
                return;
            if (menu_at_bot())
                scroll_offset_++;
            selected_item_++;
            break;
        case top:
            scroll_offset_ = selected_item_ = 0;
            break;
        case bot:
            selected_item_ = item_count() - 1;
            scroll_offset_ = selected_item_ - menu_capacity() + 1;
            break;
        }
    }

    void index::mark_item(const size_t idx) { marked_items_.insert(idx); }

    void index::unmark_item(const size_t idx) { marked_items_.erase(idx); }

    void index::toggle_action()
    {
        /* Toggle item selection. */
        if (is_marked(selected_item_))
            unmark_item(selected_item_);
        else
            mark_item(selected_item_);
    }

    void index::update_column_widths()
    {
        size_t x = 1;
        for (auto &column : columns_) {
            try {
                column.width = std::get<int>(column.width_w);
            } catch (std::bad_variant_access &) {
                /* It's a ratio, so multiply it with the full width. */
                const int width = get_width() - 1;
                column.width = width * std::get<double>(column.width_w);
            }

            column.startx = x;
            x += column.width + 3; // We want a 1 char padding on both sides of the seperator.
        }
    }

    void index::on_resize()
    {
        base::on_resize();

        update_column_widths();

        /*
         * When the window is resized from the lower left/right
         * corner, the currently selected item may escape the
         * menu, so we lock it here.
         *
         * TODO: fix this for cases where the cursor has
         * jumped more than one step down under.
         * (check whether it's just below what's valid, and
         * if so, move it to menu_bot).
         */
        if (menu_at_bot())
            selected_item_--;
    }

    int index::print_header(const columns_t::column_t &col)
    {
        int x = 0;

        /* Center the title. */
        print(col.startx + col.width / 2 - col.title.length() / 2, 0, col.title, attribute::bold, colour::blue);
        x += std::max(col.width, col.title.length());

        /* Padding between the title and the seperator to the left. */
        x++;

        /* Print the seperator. */
        print(col.startx + x++, 0, "|");

        /* Padding between the title and the seperator to the right. */
        x++;

        return x;
    }

    void index::print_column(const columns_t::column_t &col)
    {
        for (size_t i = scroll_offset_, y = 1; i < item_count() && y <= menu_capacity(); i++, y++) {

            const bool on_selected_item = (y + scroll_offset_ == selected_item_ + 1),
                       on_marked_item = is_marked(y + scroll_offset_ - 1);

            /* Print the indicator, indicating which item is currently selected. */
            if (on_selected_item && on_marked_item) {
                print(0, y, rune::double_right_angle_bracket, attribute::reverse);
            } else if (on_selected_item) {
                print(0, y, rune::double_right_angle_bracket);
            } else if (on_marked_item) {
                print(0, y, " ", attribute::reverse);
            }

            const auto str = std::invoke([&]() {
                const auto item = std::next(items_.cbegin(), i);

                switch (std::find(columns_.cbegin(), columns_.cend(), col) - columns_.cbegin()) {
                case 0:
                    return item->nonexacts.title;
                case 1: {
                    auto &y = item->exacts.year;
                    return (y == core::empty ? " " : std::to_string(y));
                }
                case 2:
                    return item->nonexacts.series;
                case 3:
                    return vector_to_string(item->nonexacts.authors);
                case 4:
                    return item->nonexacts.publisher;
                case 5:
                    return item->exacts.extension;
                default:
                    assert(false);
                }
            });

            const auto attrs = (on_selected_item || on_marked_item) ? attribute::reverse : attribute::none;

            /* Print the string, check if it was truncated. */
            const int trunc_len = printlim(col.startx, y, str, col.width, attrs);

            /*
             * Fill the space between the two column strings with inverted spaces.
             * This makes the whole line seem selected instead of only the strings.
             *
             * We start at the end of the string, just after the last character (or the
             * '~'), and write until the end of the column, plus seperator and the
             * padding on the right side of it (e.g. up to and including the first char
             * in the next column, hence the magic).
             */
            const auto string_end = col.startx + str.length() - trunc_len, next_start = col.startx + col.width + 2;
            for (auto x = string_end; x <= next_start; x++)
                print(x, y, " ", attrs);
        }
    }

    const std::pair<int, int> index::compress_to(double part)
    {
        const int details_height = menu_capacity() * (1 - part);

        /*
         * Will the detail menu hide the currently highlighted item?
         * How much do we need to scroll if we don't want that to happen?
         */
        const int scroll = std::max<int>(selected_item_ - scroll_offset_ - menu_capacity() + 1, 0);
        scroll_offset_ += scroll;

        return {scroll, details_height - 1};
    }

    void index::decompress(int scroll) { scroll_offset_ -= scroll; }

} // namespace bookwyrm::tui::screen
<commit_msg>refactor(screen/index): impove `on_resize`<commit_after>#include <fmt/format.h>
#include <ncurses.h>

#include "../../string.hpp"
#include "screens/index.hpp"
#include "screens/item_details.hpp"

namespace bookwyrm::tui::screen {

    void index::columns_t::operator=(std::vector<std::pair<std::string, column_t::width_w_t>> &&pairs)
    {
        int i = 0;
        for (auto &&pair : pairs) {
            columns_[i].width_w = std::get<1>(pair);
            columns_[i++].title = std::get<0>(pair);
        }
    }

    index::index(std::set<core::item> const &items)
        : base(default_padding_top, default_padding_bot, default_padding_left, default_padding_right), selected_item_(0),
          scroll_offset_(0), items_(items)
    {
        /*
         * These wanted widths works fine for now,
         * but we are still not utilizing the full 100%
         * of the width, which we should.
         *
         * TODO: Remedy this.
         */
        columns_ = {
            {"Title", .30},
            {"Year", 4},
            {"Series", .15},
            {"Authors", .20},
            {"Publisher", .15},
            {"Format", 6},
        };

        update_column_widths();
    }

    void index::paint()
    {
        erase();

        size_t x = 0;
        /* Start at x = 1 to align with column strings. */
        for (auto &column : columns_) {
            /* Can we fit the next header? */
            if (column.width > get_width() - x)
                break;

            x += print_header(column);
            print_column(column);
        }

        refresh();
    }

    std::string index::footer_info() const
    {
        if (plugin_count_ == 0) {
            return fmt::format("Search finished: I found {} items.", item_count());
        } else {
            return fmt::format(
                "Searching with {} running plugins... I have found {} items thus far.", plugin_count_, item_count());
        }
    }

    std::string index::controls_legacy() const { return "[j/k d/u G/g]Navigation [SPACE]Toggle select [l/->]Open details"; }

    int index::scrollpercent() const
    {
        if (item_count() <= menu_capacity())
            return scroll::not_applicable;

        return ratio(selected_item_, item_count());
    }

    void index::prepare(int plugin_count) { plugin_count_ = plugin_count; }

    bool index::is_marked(const size_t idx) const { return marked_items_.find(idx) != marked_items_.cend(); }

    size_t index::menu_capacity() const { return get_height() - 1; }

    bool index::menu_at_bot() const { return selected_item_ == (menu_capacity() - 1 + scroll_offset_); }

    bool index::menu_at_top() const { return selected_item_ == scroll_offset_; }

    void index::move(move_direction dir)
    {
        const bool at_first_item = selected_item_ == 0, at_last_item = selected_item_ == (item_count() - 1);

        switch (dir) {
        case up:
            if (at_first_item)
                return;
            if (menu_at_top())
                scroll_offset_--;
            selected_item_--;
            break;
        case down:
            if (at_last_item)
                return;
            if (menu_at_bot())
                scroll_offset_++;
            selected_item_++;
            break;
        case top:
            scroll_offset_ = selected_item_ = 0;
            break;
        case bot:
            selected_item_ = item_count() - 1;
            scroll_offset_ = selected_item_ - menu_capacity() + 1;
            break;
        }
    }

    void index::mark_item(const size_t idx) { marked_items_.insert(idx); }

    void index::unmark_item(const size_t idx) { marked_items_.erase(idx); }

    void index::toggle_action()
    {
        /* Toggle item selection. */
        if (is_marked(selected_item_))
            unmark_item(selected_item_);
        else
            mark_item(selected_item_);
    }

    void index::update_column_widths()
    {
        size_t x = 1;
        for (auto &column : columns_) {
            try {
                column.width = std::get<int>(column.width_w);
            } catch (std::bad_variant_access &) {
                /* It's a ratio, so multiply it with the full width. */
                const int width = get_width() - 1;
                column.width = width * std::get<double>(column.width_w);
            }

            column.startx = x;
            x += column.width + 3; // We want a 1 char padding on both sides of the seperator.
        }
    }

    void index::on_resize()
    {
        base::on_resize();

        update_column_widths();

        /* Ensure we don't select an item past the tail. */
        if (auto tail = scroll_offset_ + menu_capacity() - 1; selected_item_ > tail)
            selected_item_ = tail;
    }

    int index::print_header(const columns_t::column_t &col)
    {
        int x = 0;

        /* Center the title. */
        print(col.startx + col.width / 2 - col.title.length() / 2, 0, col.title, attribute::bold, colour::blue);
        x += std::max(col.width, col.title.length());

        /* Padding between the title and the seperator to the left. */
        x++;

        /* Print the seperator. */
        print(col.startx + x++, 0, "|");

        /* Padding between the title and the seperator to the right. */
        x++;

        return x;
    }

    void index::print_column(const columns_t::column_t &col)
    {
        for (size_t i = scroll_offset_, y = 1; i < item_count() && y <= menu_capacity(); i++, y++) {

            const bool on_selected_item = (y + scroll_offset_ == selected_item_ + 1),
                       on_marked_item = is_marked(y + scroll_offset_ - 1);

            /* Print the indicator, indicating which item is currently selected. */
            if (on_selected_item && on_marked_item) {
                print(0, y, rune::double_right_angle_bracket, attribute::reverse);
            } else if (on_selected_item) {
                print(0, y, rune::double_right_angle_bracket);
            } else if (on_marked_item) {
                print(0, y, " ", attribute::reverse);
            }

            const auto str = std::invoke([&]() {
                const auto item = std::next(items_.cbegin(), i);

                switch (std::find(columns_.cbegin(), columns_.cend(), col) - columns_.cbegin()) {
                case 0:
                    return item->nonexacts.title;
                case 1: {
                    auto &y = item->exacts.year;
                    return (y == core::empty ? " " : std::to_string(y));
                }
                case 2:
                    return item->nonexacts.series;
                case 3:
                    return vector_to_string(item->nonexacts.authors);
                case 4:
                    return item->nonexacts.publisher;
                case 5:
                    return item->exacts.extension;
                default:
                    assert(false);
                }
            });

            const auto attrs = (on_selected_item || on_marked_item) ? attribute::reverse : attribute::none;

            /* Print the string, check if it was truncated. */
            const int trunc_len = printlim(col.startx, y, str, col.width, attrs);

            /*
             * Fill the space between the two column strings with inverted spaces.
             * This makes the whole line seem selected instead of only the strings.
             *
             * We start at the end of the string, just after the last character (or the
             * '~'), and write until the end of the column, plus seperator and the
             * padding on the right side of it (e.g. up to and including the first char
             * in the next column, hence the magic).
             */
            const auto string_end = col.startx + str.length() - trunc_len, next_start = col.startx + col.width + 2;
            for (auto x = string_end; x <= next_start; x++)
                print(x, y, " ", attrs);
        }
    }

    const std::pair<int, int> index::compress_to(double part)
    {
        const int details_height = menu_capacity() * (1 - part);

        /*
         * Will the detail menu hide the currently highlighted item?
         * How much do we need to scroll if we don't want that to happen?
         */
        const int scroll = std::max<int>(selected_item_ - scroll_offset_ - menu_capacity() + 1, 0);
        scroll_offset_ += scroll;

        return {scroll, details_height - 1};
    }

    void index::decompress(int scroll) { scroll_offset_ -= scroll; }

} // namespace bookwyrm::tui::screen
<|endoftext|>


<commit_before>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <sys/stat.h>
#include <algorithm>
#include <deque>

#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/lib/gtl/map_util.h"
#include "tensorflow/core/lib/gtl/stl_util.h"
#include "tensorflow/core/lib/io/path.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/file_system.h"
#include "tensorflow/core/platform/platform.h"
#include "tensorflow/core/platform/protobuf.h"

namespace tensorflow {

namespace {

constexpr int kNumThreads = 8;

// Run a function in parallel using a ThreadPool, but skip the ThreadPool
// on the iOS platform due to its problems with more than a few threads.
void ForEach(int first, int last, std::function<void(int)> f) {
#if defined(__ANDROID__) || defined(TARGET_OS_IPHONE)
  for (int i = first; i < last; i++) {
    f(i);
  }
#else
  int num_threads = std::min(kNumThreads, last - first);
  thread::ThreadPool threads(Env::Default(), "ForEach", num_threads);
  for (int i = first; i < last; i++) {
    threads.Schedule([f, i] { f(i); });
  }
#endif
}

}  // anonymous namespace

FileSystem::~FileSystem() {}

string FileSystem::TranslateName(const string& name) const {
  return io::CleanPath(name);
}

Status FileSystem::IsDirectory(const string& name) {
  // Check if path exists.
  if (!FileExists(name)) {
    return Status(tensorflow::error::NOT_FOUND, "Path not found");
  }
  FileStatistics stat;
  TF_RETURN_IF_ERROR(Stat(name, &stat));
  if (stat.is_directory) {
    return Status::OK();
  }
  return Status(tensorflow::error::FAILED_PRECONDITION, "Not a directory");
}

RandomAccessFile::~RandomAccessFile() {}

WritableFile::~WritableFile() {}

FileSystemRegistry::~FileSystemRegistry() {}

Status FileSystem::GetMatchingPaths(const string& pattern,
                                    std::vector<string>* results) {
  results->clear();
  // Find the fixed prefix by looking for the first wildcard.
  const string& fixed_prefix =
      pattern.substr(0, pattern.find_first_of("*?[\\"));
  std::vector<string> all_files;
  string dir = io::Dirname(fixed_prefix).ToString();
  if (dir.empty()) dir = ".";

  // Setup a BFS to explore everything under dir.
  std::deque<string> dir_q;
  dir_q.push_back(dir);
  Status ret;  // Status to return.
  // children_dir_status holds is_dir status for children. The ints are used
  // as booleans.
  // Note: children_dir_status can't be declared as a std::vector<bool>.
  // std::vector has a specialization for the type bool. std::vector<bool> is
  // implemented as a bitset and accesses to elements are not atomic.
  std::vector<int> children_dir_status;
  while (!dir_q.empty()) {
    string current_dir = dir_q.front();
    dir_q.pop_front();
    std::vector<string> children;
    Status s = GetChildren(current_dir, &children);
    ret.Update(s);
    if (children.empty()) continue;
    // This IsDirectory call can be expensive for some FS. Parallelizing it.
    children_dir_status.resize(children.size());
    ForEach(0, children.size(),
            [this, &current_dir, &children, &children_dir_status](int i) {
              const string child_path = io::JoinPath(current_dir, children[i]);
              children_dir_status[i] = IsDirectory(child_path).ok();
            });
    for (int i = 0; i < children.size(); ++i) {
      const string child_path = io::JoinPath(current_dir, children[i]);
      // In case the child_path doesn't start with the fixed_prefix then we bail
      // and don't add it to the queue / candidates.
      if (!StringPiece(child_path).starts_with(fixed_prefix)) continue;
      // If the child is a directory add it to the queue.
      if (children_dir_status[i]) {
        dir_q.push_back(child_path);
      }
      all_files.push_back(child_path);
    }
  }

  // Match all obtained files to the input pattern.
  for (const auto& f : all_files) {
    if (Env::Default()->MatchPath(f, pattern)) {
      results->push_back(f);
    }
  }
  return ret;
}

Status FileSystem::DeleteRecursively(const string& dirname,
                                     int64* undeleted_files,
                                     int64* undeleted_dirs) {
  CHECK_NOTNULL(undeleted_files);
  CHECK_NOTNULL(undeleted_dirs);

  *undeleted_files = 0;
  *undeleted_dirs = 0;
  // Make sure that dirname exists;
  if (!FileExists(dirname)) {
    (*undeleted_dirs)++;
    return Status(error::NOT_FOUND, "Directory doesn't exist");
  }
  std::deque<string> dir_q;      // Queue for the BFS
  std::vector<string> dir_list;  // List of all dirs discovered
  dir_q.push_back(dirname);
  Status ret;  // Status to be returned.
  // Do a BFS on the directory to discover all the sub-directories. Remove all
  // children that are files along the way. Then cleanup and remove the
  // directories in reverse order.;
  while (!dir_q.empty()) {
    string dir = dir_q.front();
    dir_q.pop_front();
    dir_list.push_back(dir);
    std::vector<string> children;
    // GetChildren might fail if we don't have appropriate permissions.
    Status s = GetChildren(dir, &children);
    ret.Update(s);
    if (!s.ok()) {
      (*undeleted_dirs)++;
      continue;
    }
    for (const string& child : children) {
      const string child_path = io::JoinPath(dir, child);
      // If the child is a directory add it to the queue, otherwise delete it.
      if (IsDirectory(child_path).ok()) {
        dir_q.push_back(child_path);
      } else {
        // Delete file might fail because of permissions issues or might be
        // unimplemented.
        Status del_status = DeleteFile(child_path);
        ret.Update(del_status);
        if (!del_status.ok()) {
          (*undeleted_files)++;
        }
      }
    }
  }
  // Now reverse the list of directories and delete them. The BFS ensures that
  // we can delete the directories in this order.
  std::reverse(dir_list.begin(), dir_list.end());
  for (const string& dir : dir_list) {
    // Delete dir might fail because of permissions issues or might be
    // unimplemented.
    Status s = DeleteDir(dir);
    ret.Update(s);
    if (!s.ok()) {
      (*undeleted_dirs)++;
    }
  }
  return ret;
}

Status FileSystem::RecursivelyCreateDir(const string& dirname) {
  StringPiece scheme, host, remaining_dir;
  io::ParseURI(dirname, &scheme, &host, &remaining_dir);
  std::vector<StringPiece> sub_dirs;
  while (!FileExists(io::CreateURI(scheme, host, remaining_dir)) &&
         !remaining_dir.empty()) {
    // Basename returns "" for / ending dirs.
    if (!remaining_dir.ends_with("/")) {
      sub_dirs.push_back(io::Basename(remaining_dir));
    }
    remaining_dir = io::Dirname(remaining_dir);
  }

  // sub_dirs contains all the dirs to be created but in reverse order.
  std::reverse(sub_dirs.begin(), sub_dirs.end());

  // Now create the directories.
  string built_path = remaining_dir.ToString();
  for (const StringPiece sub_dir : sub_dirs) {
    built_path = io::JoinPath(built_path, sub_dir);
    TF_RETURN_IF_ERROR(CreateDir(io::CreateURI(scheme, host, built_path)));
  }
  return Status::OK();
}

}  // namespace tensorflow
<commit_msg>Refining the condition for parallel IsDirectory(). Avoiding "defined()" for TARGET_OS_IPHONE since it is often defined as zero.  Removing "defined(__ANDROID__)" since the problem with the android emulator cannot be reproduced now. Change: 138138237<commit_after>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <sys/stat.h>
#include <algorithm>
#include <deque>

#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/lib/gtl/map_util.h"
#include "tensorflow/core/lib/gtl/stl_util.h"
#include "tensorflow/core/lib/io/path.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/file_system.h"
#include "tensorflow/core/platform/platform.h"
#include "tensorflow/core/platform/protobuf.h"

namespace tensorflow {

namespace {

constexpr int kNumThreads = 8;

// Run a function in parallel using a ThreadPool, but skip the ThreadPool
// on the iOS platform due to its problems with more than a few threads.
void ForEach(int first, int last, std::function<void(int)> f) {
#if TARGET_OS_IPHONE
  for (int i = first; i < last; i++) {
    f(i);
  }
#else
  int num_threads = std::min(kNumThreads, last - first);
  thread::ThreadPool threads(Env::Default(), "ForEach", num_threads);
  for (int i = first; i < last; i++) {
    threads.Schedule([f, i] { f(i); });
  }
#endif
}

}  // anonymous namespace

FileSystem::~FileSystem() {}

string FileSystem::TranslateName(const string& name) const {
  return io::CleanPath(name);
}

Status FileSystem::IsDirectory(const string& name) {
  // Check if path exists.
  if (!FileExists(name)) {
    return Status(tensorflow::error::NOT_FOUND, "Path not found");
  }
  FileStatistics stat;
  TF_RETURN_IF_ERROR(Stat(name, &stat));
  if (stat.is_directory) {
    return Status::OK();
  }
  return Status(tensorflow::error::FAILED_PRECONDITION, "Not a directory");
}

RandomAccessFile::~RandomAccessFile() {}

WritableFile::~WritableFile() {}

FileSystemRegistry::~FileSystemRegistry() {}

Status FileSystem::GetMatchingPaths(const string& pattern,
                                    std::vector<string>* results) {
  results->clear();
  // Find the fixed prefix by looking for the first wildcard.
  const string& fixed_prefix =
      pattern.substr(0, pattern.find_first_of("*?[\\"));
  std::vector<string> all_files;
  string dir = io::Dirname(fixed_prefix).ToString();
  if (dir.empty()) dir = ".";

  // Setup a BFS to explore everything under dir.
  std::deque<string> dir_q;
  dir_q.push_back(dir);
  Status ret;  // Status to return.
  // children_dir_status holds is_dir status for children. The ints are used
  // as booleans.
  // Note: children_dir_status can't be declared as a std::vector<bool>.
  // std::vector has a specialization for the type bool. std::vector<bool> is
  // implemented as a bitset and accesses to elements are not atomic.
  std::vector<int> children_dir_status;
  while (!dir_q.empty()) {
    string current_dir = dir_q.front();
    dir_q.pop_front();
    std::vector<string> children;
    Status s = GetChildren(current_dir, &children);
    ret.Update(s);
    if (children.empty()) continue;
    // This IsDirectory call can be expensive for some FS. Parallelizing it.
    children_dir_status.resize(children.size());
    ForEach(0, children.size(),
            [this, &current_dir, &children, &children_dir_status](int i) {
              const string child_path = io::JoinPath(current_dir, children[i]);
              children_dir_status[i] = IsDirectory(child_path).ok();
            });
    for (int i = 0; i < children.size(); ++i) {
      const string child_path = io::JoinPath(current_dir, children[i]);
      // In case the child_path doesn't start with the fixed_prefix then we bail
      // and don't add it to the queue / candidates.
      if (!StringPiece(child_path).starts_with(fixed_prefix)) continue;
      // If the child is a directory add it to the queue.
      if (children_dir_status[i]) {
        dir_q.push_back(child_path);
      }
      all_files.push_back(child_path);
    }
  }

  // Match all obtained files to the input pattern.
  for (const auto& f : all_files) {
    if (Env::Default()->MatchPath(f, pattern)) {
      results->push_back(f);
    }
  }
  return ret;
}

Status FileSystem::DeleteRecursively(const string& dirname,
                                     int64* undeleted_files,
                                     int64* undeleted_dirs) {
  CHECK_NOTNULL(undeleted_files);
  CHECK_NOTNULL(undeleted_dirs);

  *undeleted_files = 0;
  *undeleted_dirs = 0;
  // Make sure that dirname exists;
  if (!FileExists(dirname)) {
    (*undeleted_dirs)++;
    return Status(error::NOT_FOUND, "Directory doesn't exist");
  }
  std::deque<string> dir_q;      // Queue for the BFS
  std::vector<string> dir_list;  // List of all dirs discovered
  dir_q.push_back(dirname);
  Status ret;  // Status to be returned.
  // Do a BFS on the directory to discover all the sub-directories. Remove all
  // children that are files along the way. Then cleanup and remove the
  // directories in reverse order.;
  while (!dir_q.empty()) {
    string dir = dir_q.front();
    dir_q.pop_front();
    dir_list.push_back(dir);
    std::vector<string> children;
    // GetChildren might fail if we don't have appropriate permissions.
    Status s = GetChildren(dir, &children);
    ret.Update(s);
    if (!s.ok()) {
      (*undeleted_dirs)++;
      continue;
    }
    for (const string& child : children) {
      const string child_path = io::JoinPath(dir, child);
      // If the child is a directory add it to the queue, otherwise delete it.
      if (IsDirectory(child_path).ok()) {
        dir_q.push_back(child_path);
      } else {
        // Delete file might fail because of permissions issues or might be
        // unimplemented.
        Status del_status = DeleteFile(child_path);
        ret.Update(del_status);
        if (!del_status.ok()) {
          (*undeleted_files)++;
        }
      }
    }
  }
  // Now reverse the list of directories and delete them. The BFS ensures that
  // we can delete the directories in this order.
  std::reverse(dir_list.begin(), dir_list.end());
  for (const string& dir : dir_list) {
    // Delete dir might fail because of permissions issues or might be
    // unimplemented.
    Status s = DeleteDir(dir);
    ret.Update(s);
    if (!s.ok()) {
      (*undeleted_dirs)++;
    }
  }
  return ret;
}

Status FileSystem::RecursivelyCreateDir(const string& dirname) {
  StringPiece scheme, host, remaining_dir;
  io::ParseURI(dirname, &scheme, &host, &remaining_dir);
  std::vector<StringPiece> sub_dirs;
  while (!FileExists(io::CreateURI(scheme, host, remaining_dir)) &&
         !remaining_dir.empty()) {
    // Basename returns "" for / ending dirs.
    if (!remaining_dir.ends_with("/")) {
      sub_dirs.push_back(io::Basename(remaining_dir));
    }
    remaining_dir = io::Dirname(remaining_dir);
  }

  // sub_dirs contains all the dirs to be created but in reverse order.
  std::reverse(sub_dirs.begin(), sub_dirs.end());

  // Now create the directories.
  string built_path = remaining_dir.ToString();
  for (const StringPiece sub_dir : sub_dirs) {
    built_path = io::JoinPath(built_path, sub_dir);
    TF_RETURN_IF_ERROR(CreateDir(io::CreateURI(scheme, host, built_path)));
  }
  return Status::OK();
}

}  // namespace tensorflow
<|endoftext|>


<commit_before>#pragma once

#include <memory>
#include <type_traits>
#include <agency/detail/type_traits.hpp>
#include <agency/cuda/detail/launch_kernel.hpp>
#include <agency/cuda/detail/workaround_unused_variable_warning.hpp>

// XXX should eliminate this dependency on Thrust
#include <thrust/system/cuda/memory.h>
#include <thrust/detail/swap.h>

namespace agency
{
namespace cuda
{
namespace detail
{

template<class T>
__global__ void destroy_kernel(T* ptr)
{
  ptr->~T();
}

template<class T, class... Args>
__global__ void construct_kernel(T* ptr, Args... args)
{
  ::new(ptr) T(args...);
}

template<class T>
class default_delete
{
  public:
    __host__ __device__
    default_delete(cudaStream_t s)
      : stream_(s)
    {}

    __host__ __device__
    cudaStream_t stream() const
    {
      return stream_;
    }

    __host__ __device__
    void operator()(T* ptr) const
    {
      auto kernel = detail::destroy_kernel<T>;
      detail::workaround_unused_variable_warning(kernel);

#ifndef __CUDA_ARCH__
      // we're executing on the host; launch a kernel to call the destructor
      detail::launch_kernel(reinterpret_cast<void*>(kernel), ::dim3{1,1,1}, ::dim3{1,1,1}, 0, stream(), ptr);
#else
      // we're executing on the device; just call the destructor directly
      ptr->~T();
#endif

      // deallocate
      thrust::cuda::free(thrust::cuda::pointer<T>(ptr));
    }

  private:
    cudaStream_t stream_;
};

template<class T>
class unique_ptr
{
  public:
    using element_type = agency::detail::decay_t<T>;
    using pointer      = element_type*;
    using deleter_type = default_delete<element_type>;

    __host__ __device__
    unique_ptr(thrust::cuda::pointer<T> ptr, const deleter_type& deleter = default_delete<T>())
      : ptr_(ptr),
        deleter_(deleter)
    {}

    __host__ __device__
    unique_ptr(unique_ptr&& other)
      : ptr_(),
        deleter_(std::move(other.get_deleter()))
    {
      thrust::swap(ptr_, other.ptr_);
    }

    __host__ __device__
    ~unique_ptr()
    {
      if(this->get())
      {
        get_deleter()(this->get());
      }
    }

    __host__ __device__
    unique_ptr& operator=(unique_ptr&& other)
    {
      thrust::swap(ptr_, other.ptr_);
      return *this;
    }

    __host__ __device__
    pointer get() const
    {
      return ptr_.get();
    }

    __host__ __device__
    pointer release()
    {
      thrust::cuda::pointer<element_type> result;
      thrust::swap(ptr_, result);
      return result.get();
    }

    __host__ __device__
    deleter_type& get_deleter()
    {
      return deleter_;
    }

    __host__ __device__
    const deleter_type& get_deleter() const
    {
      return deleter_;
    }

    __host__ __device__
    T operator*() const
    {
      return *ptr_;
    }

  private:
    thrust::cuda::pointer<T> ptr_;
    default_delete<T> deleter_;
};

template<class T, class... Args>
__host__ __device__
unique_ptr<T> make_unique(cudaStream_t s, Args&&... args)
{
  auto deleter = default_delete<T>(s);
  unique_ptr<T> result(thrust::cuda::malloc<T>(1), deleter);

  // XXX nvcc 7.0 has trouble with this decay_t sometimes
  //auto kernel = detail::construct_kernel<T,agency::detail::decay_t<Args>...>;
  auto kernel = detail::construct_kernel<T,typename std::decay<Args>::type...>;
  detail::workaround_unused_variable_warning(kernel);

#ifndef __CUDA_ARCH__
  // we're executing on the host; launch a kernel to call the constructor
  detail::checked_launch_kernel(reinterpret_cast<void*>(kernel), ::dim3{1,1,1}, ::dim3{1,1,1}, 0, s, result.get(), std::forward<Args>(args)...);
#else
  // we're executing on the device; just placement new directly
  ::new(result.get()) T(std::forward<Args>(args)...);
#endif

  return std::move(result);
}

} // end detail
} // end cuda
} // end agency

<commit_msg>Allow unique_ptr to convert to bool<commit_after>#pragma once

#include <memory>
#include <type_traits>
#include <agency/detail/type_traits.hpp>
#include <agency/cuda/detail/launch_kernel.hpp>
#include <agency/cuda/detail/workaround_unused_variable_warning.hpp>

// XXX should eliminate this dependency on Thrust
#include <thrust/system/cuda/memory.h>
#include <thrust/detail/swap.h>

namespace agency
{
namespace cuda
{
namespace detail
{

template<class T>
__global__ void destroy_kernel(T* ptr)
{
  ptr->~T();
}

template<class T, class... Args>
__global__ void construct_kernel(T* ptr, Args... args)
{
  ::new(ptr) T(args...);
}

template<class T>
class default_delete
{
  public:
    __host__ __device__
    default_delete(cudaStream_t s)
      : stream_(s)
    {}

    __host__ __device__
    cudaStream_t stream() const
    {
      return stream_;
    }

    __host__ __device__
    void operator()(T* ptr) const
    {
      auto kernel = detail::destroy_kernel<T>;
      detail::workaround_unused_variable_warning(kernel);

#ifndef __CUDA_ARCH__
      // we're executing on the host; launch a kernel to call the destructor
      detail::launch_kernel(reinterpret_cast<void*>(kernel), ::dim3{1,1,1}, ::dim3{1,1,1}, 0, stream(), ptr);
#else
      // we're executing on the device; just call the destructor directly
      ptr->~T();
#endif

      // deallocate
      thrust::cuda::free(thrust::cuda::pointer<T>(ptr));
    }

  private:
    cudaStream_t stream_;
};

template<class T>
class unique_ptr
{
  public:
    using element_type = agency::detail::decay_t<T>;
    using pointer      = element_type*;
    using deleter_type = default_delete<element_type>;

    __host__ __device__
    unique_ptr(thrust::cuda::pointer<T> ptr, const deleter_type& deleter = default_delete<T>())
      : ptr_(ptr),
        deleter_(deleter)
    {}

    __host__ __device__
    unique_ptr(unique_ptr&& other)
      : ptr_(),
        deleter_(std::move(other.get_deleter()))
    {
      thrust::swap(ptr_, other.ptr_);
    }

    __host__ __device__
    ~unique_ptr()
    {
      if(this->get())
      {
        get_deleter()(this->get());
      }
    }

    __host__ __device__
    unique_ptr& operator=(unique_ptr&& other)
    {
      thrust::swap(ptr_, other.ptr_);
      return *this;
    }

    __host__ __device__
    pointer get() const
    {
      return ptr_.get();
    }

    __host__ __device__
    pointer release()
    {
      thrust::cuda::pointer<element_type> result;
      thrust::swap(ptr_, result);
      return result.get();
    }

    __host__ __device__
    deleter_type& get_deleter()
    {
      return deleter_;
    }

    __host__ __device__
    const deleter_type& get_deleter() const
    {
      return deleter_;
    }

    __host__ __device__
    T operator*() const
    {
      return *ptr_;
    }

    __host__ __device__
    operator bool () const
    {
      return get();
    }

  private:
    thrust::cuda::pointer<T> ptr_;
    default_delete<T> deleter_;
};

template<class T, class... Args>
__host__ __device__
unique_ptr<T> make_unique(cudaStream_t s, Args&&... args)
{
  auto deleter = default_delete<T>(s);
  unique_ptr<T> result(thrust::cuda::malloc<T>(1), deleter);

  // XXX nvcc 7.0 has trouble with this decay_t sometimes
  //auto kernel = detail::construct_kernel<T,agency::detail::decay_t<Args>...>;
  auto kernel = detail::construct_kernel<T,typename std::decay<Args>::type...>;
  detail::workaround_unused_variable_warning(kernel);

#ifndef __CUDA_ARCH__
  // we're executing on the host; launch a kernel to call the constructor
  detail::checked_launch_kernel(reinterpret_cast<void*>(kernel), ::dim3{1,1,1}, ::dim3{1,1,1}, 0, s, result.get(), std::forward<Args>(args)...);
#else
  // we're executing on the device; just placement new directly
  ::new(result.get()) T(std::forward<Args>(args)...);
#endif

  return std::move(result);
}

} // end detail
} // end cuda
} // end agency

<|endoftext|>