<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: mvsave.hxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-09 03:51:06 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _MVSAVE_HXX
#define _MVSAVE_HXX

#ifndef _STRING_HXX //autogen
#include <tools/string.hxx>
#endif
#ifndef _KEYCOD_HXX //autogen
#include <vcl/keycod.hxx>
#endif
#ifndef _SVARRAY_HXX //autogen
#include <svtools/svarray.hxx>
#endif

class SvNumberFormatter;
class SvULongs;
class SwBookmark;
class SwDoc;
class SwFmtAnchor;
class SwFrmFmt;
class SwIndex;
class SwNodeIndex;
class SwNodeRange;
class SwPaM;
class SwRedline;
struct SwPosition;

enum SaveBookmarkType { BKMK_POS_NONE   = 0x00,
                        BKMK_POS        = 0x01,
                        BKMK_POS_OTHER  = 0x02
                         };

class SaveBookmark
{
    String  aName, aShortName;
    ULONG nNode1, nNode2;
    xub_StrLen nCntnt1, nCntnt2;
    KeyCode aCode;
    SaveBookmarkType eBkmkType;
    BOOKMARK_TYPE    eOrigBkmType;

public:
    SaveBookmark( int, const SwBookmark&, const SwNodeIndex&,
                                    const SwIndex* pIdx = 0 );
    void SetInDoc( SwDoc* pDoc, const SwNodeIndex&, const SwIndex* pIdx = 0);
    BOOKMARK_TYPE   GetOriginalBkmType() const {return eOrigBkmType;}
};

SV_DECL_PTRARR_DEL( SaveBookmarks, SaveBookmark*, 0, 10 )

void _DelBookmarks( const SwNodeIndex& rStt,
                    const SwNodeIndex& rEnd,
                    SaveBookmarks* pSaveBkmk = 0,
                    const SwIndex* pSttIdx = 0,
                    const SwIndex* pEndIdx = 0 );

#define SAVEFLY 1
#define SAVEFLY_SPLIT 2

void _SaveCntntIdx( SwDoc* pDoc, ULONG nNode, xub_StrLen nCntnt,
                    SvULongs& rSaveArr, BYTE nSaveFly = 0 );
void _RestoreCntntIdx( SwDoc* pDoc, SvULongs& rSaveArr,
                        ULONG nNode, xub_StrLen nOffset = 0,
                        BOOL bAuto = FALSE );
void _RestoreCntntIdx( SvULongs& rSaveArr, const SwNode& rNd,
                        xub_StrLen nLen, xub_StrLen nCorrLen );

/** data structure to temporarily hold fly anchor positions relative to some
 *  location. */
struct _SaveFly
{
    ULONG nNdDiff;              /// relative node difference
    SwFrmFmt* pFrmFmt;          /// the fly's frame format
    sal_Bool bInsertPosition;   /// if true, anchor _at_ insert position

    _SaveFly( ULONG nNodeDiff, SwFrmFmt* pFmt, sal_Bool bInsert )
        : nNdDiff( nNodeDiff ), pFrmFmt( pFmt ), bInsertPosition( bInsert )
    { }
};

SV_DECL_VARARR( _SaveFlyArr, _SaveFly, 0, 10 )

void _RestFlyInRange( _SaveFlyArr& rArr, const SwNodeIndex& rSttIdx,
                      const SwNodeIndex* pInsPos );
void _SaveFlyInRange( const SwNodeRange& rRg, _SaveFlyArr& rArr );
void _SaveFlyInRange( const SwPaM& rPam, const SwNodeIndex& rInsPos,
                       _SaveFlyArr& rArr, sal_Bool bMoveAllFlys );

void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
                    const SwNodeIndex& rPtNdIdx );

class SwDataChanged
{
    const SwPaM* pPam;
    const SwPosition* pPos;
    SwDoc* pDoc;
    ULONG nNode;
    xub_StrLen nCntnt;
    USHORT nType;       // Insert/Move/Delete/... (UndoIds)

public:
    SwDataChanged( const SwPaM& rPam, USHORT nType );
    SwDataChanged( SwDoc* pDoc, const SwPosition& rPos, USHORT nType );
    ~SwDataChanged();

    ULONG GetNode() const           { return nNode; }
    xub_StrLen GetCntnt() const     { return nCntnt; }
};

// Funktions-Deklaration damit auch alles unter der CrsrShell mal die
// Crsr verschieben kann
// die Funktionen rufen nicht die SwDoc::Corr - Methoden!

    // Setzt alle PaMs an OldPos auf NewPos + Offset
void PaMCorrAbs( const SwPosition &rOldPos,
                const SwPosition &rNewPos,
                const xub_StrLen nOffset = 0 );

    // Setzt alle PaMs in OldNode auf NewPos + Offset
void PaMCorrAbs( const SwNodeIndex &rOldNode,
                const SwPosition &rNewPos,
                const xub_StrLen nOffset = 0 );

    // Setzt alle PaMs im Bereich vom Range nach NewPos
void PaMCorrAbs( const SwPaM& rRange,
                 const SwPosition& rNewPos );

    // Setzt alle PaMs im Bereich von [StartNode, EndNode] nach NewPos
void PaMCorrAbs( const SwNodeIndex &rStartNode,
                 const SwNodeIndex &rEndNode,
                 const SwPosition &rNewPos );

    // Setzt alle PaMs in OldNode auf relative Pos
void PaMCorrRel( const SwNodeIndex &rOldNode,
                 const SwPosition &rNewPos,
                 const xub_StrLen nOffset = 0 );

// Hilfsklasse zum kopieren von absatzgebundenen Flys. Durch die Sortierung
// nach der Ordnungsnummer wird versucht die layout seitige Anordnung
// bei zu behalten
class _ZSortFly
{
    const SwFrmFmt* pFmt;
    const SwFmtAnchor* pAnchor;
    UINT32 nOrdNum;

public:
    _ZSortFly( const SwFrmFmt* pFrmFmt, const SwFmtAnchor* pFlyAnchor,
                UINT32 nArrOrdNum );
    _ZSortFly& operator=( const _ZSortFly& rCpy )
    {
        pFmt = rCpy.pFmt, pAnchor = rCpy.pAnchor, nOrdNum = rCpy.nOrdNum;
        return *this;
    }

    int operator==( const _ZSortFly& ) const { return FALSE; }
    int operator<( const _ZSortFly& rCmp ) const
        { return nOrdNum < rCmp.nOrdNum; }

    const SwFrmFmt* GetFmt() const              { return pFmt; }
    const SwFmtAnchor* GetAnchor() const        { return pAnchor; }
};

SV_DECL_VARARR_SORT( _ZSortFlys, _ZSortFly, 0, 10 )

class SwTblNumFmtMerge
{
    SvNumberFormatter* pNFmt;
public:
    SwTblNumFmtMerge( const SwDoc& rSrc, SwDoc& rDest );
    ~SwTblNumFmtMerge();
};

class _SaveRedlEndPosForRestore
{
    SvPtrarr* pSavArr;
    SwNodeIndex* pSavIdx;

    void _Restore();
public:
    _SaveRedlEndPosForRestore( const SwNodeIndex& rInsIdx );
    ~_SaveRedlEndPosForRestore();
    void Restore() { if( pSavArr ) _Restore(); }
};

#endif  // _MVSAVE_HXX

<commit_msg>INTEGRATION: CWS redline01 (1.5.128); FILE MERGED 2006/03/22 13:14:20 ama 1.5.128.1: Fix #i59534: Text insertion at the end of a redline<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: mvsave.hxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: hr $ $Date: 2006-04-19 14:20:27 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _MVSAVE_HXX
#define _MVSAVE_HXX

#ifndef _STRING_HXX //autogen
#include <tools/string.hxx>
#endif
#ifndef _KEYCOD_HXX //autogen
#include <vcl/keycod.hxx>
#endif
#ifndef _SVARRAY_HXX //autogen
#include <svtools/svarray.hxx>
#endif

class SvNumberFormatter;
class SvULongs;
class SwBookmark;
class SwDoc;
class SwFmtAnchor;
class SwFrmFmt;
class SwIndex;
class SwNodeIndex;
class SwNodeRange;
class SwPaM;
class SwRedline;
struct SwPosition;

enum SaveBookmarkType { BKMK_POS_NONE   = 0x00,
                        BKMK_POS        = 0x01,
                        BKMK_POS_OTHER  = 0x02
                         };

class SaveBookmark
{
    String  aName, aShortName;
    ULONG nNode1, nNode2;
    xub_StrLen nCntnt1, nCntnt2;
    KeyCode aCode;
    SaveBookmarkType eBkmkType;
    BOOKMARK_TYPE    eOrigBkmType;

public:
    SaveBookmark( int, const SwBookmark&, const SwNodeIndex&,
                                    const SwIndex* pIdx = 0 );
    void SetInDoc( SwDoc* pDoc, const SwNodeIndex&, const SwIndex* pIdx = 0);
    BOOKMARK_TYPE   GetOriginalBkmType() const {return eOrigBkmType;}
};

SV_DECL_PTRARR_DEL( SaveBookmarks, SaveBookmark*, 0, 10 )

void _DelBookmarks( const SwNodeIndex& rStt,
                    const SwNodeIndex& rEnd,
                    SaveBookmarks* pSaveBkmk = 0,
                    const SwIndex* pSttIdx = 0,
                    const SwIndex* pEndIdx = 0 );

#define SAVEFLY 1
#define SAVEFLY_SPLIT 2

void _SaveCntntIdx( SwDoc* pDoc, ULONG nNode, xub_StrLen nCntnt,
                    SvULongs& rSaveArr, BYTE nSaveFly = 0 );
void _RestoreCntntIdx( SwDoc* pDoc, SvULongs& rSaveArr,
                        ULONG nNode, xub_StrLen nOffset = 0,
                        BOOL bAuto = FALSE );
void _RestoreCntntIdx( SvULongs& rSaveArr, const SwNode& rNd,
                        xub_StrLen nLen, xub_StrLen nCorrLen );

/** data structure to temporarily hold fly anchor positions relative to some
 *  location. */
struct _SaveFly
{
    ULONG nNdDiff;              /// relative node difference
    SwFrmFmt* pFrmFmt;          /// the fly's frame format
    sal_Bool bInsertPosition;   /// if true, anchor _at_ insert position

    _SaveFly( ULONG nNodeDiff, SwFrmFmt* pFmt, sal_Bool bInsert )
        : nNdDiff( nNodeDiff ), pFrmFmt( pFmt ), bInsertPosition( bInsert )
    { }
};

SV_DECL_VARARR( _SaveFlyArr, _SaveFly, 0, 10 )

void _RestFlyInRange( _SaveFlyArr& rArr, const SwNodeIndex& rSttIdx,
                      const SwNodeIndex* pInsPos );
void _SaveFlyInRange( const SwNodeRange& rRg, _SaveFlyArr& rArr );
void _SaveFlyInRange( const SwPaM& rPam, const SwNodeIndex& rInsPos,
                       _SaveFlyArr& rArr, sal_Bool bMoveAllFlys );

void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
                    const SwNodeIndex& rPtNdIdx );

class SwDataChanged
{
    const SwPaM* pPam;
    const SwPosition* pPos;
    SwDoc* pDoc;
    ULONG nNode;
    xub_StrLen nCntnt;
    USHORT nType;       // Insert/Move/Delete/... (UndoIds)

public:
    SwDataChanged( const SwPaM& rPam, USHORT nType );
    SwDataChanged( SwDoc* pDoc, const SwPosition& rPos, USHORT nType );
    ~SwDataChanged();

    ULONG GetNode() const           { return nNode; }
    xub_StrLen GetCntnt() const     { return nCntnt; }
};

// Funktions-Deklaration damit auch alles unter der CrsrShell mal die
// Crsr verschieben kann
// die Funktionen rufen nicht die SwDoc::Corr - Methoden!

    // Setzt alle PaMs an OldPos auf NewPos + Offset
void PaMCorrAbs( const SwPosition &rOldPos,
                const SwPosition &rNewPos,
                const xub_StrLen nOffset = 0 );

    // Setzt alle PaMs in OldNode auf NewPos + Offset
void PaMCorrAbs( const SwNodeIndex &rOldNode,
                const SwPosition &rNewPos,
                const xub_StrLen nOffset = 0 );

    // Setzt alle PaMs im Bereich vom Range nach NewPos
void PaMCorrAbs( const SwPaM& rRange,
                 const SwPosition& rNewPos );

    // Setzt alle PaMs im Bereich von [StartNode, EndNode] nach NewPos
void PaMCorrAbs( const SwNodeIndex &rStartNode,
                 const SwNodeIndex &rEndNode,
                 const SwPosition &rNewPos );

    // Setzt alle PaMs in OldNode auf relative Pos
void PaMCorrRel( const SwNodeIndex &rOldNode,
                 const SwPosition &rNewPos,
                 const xub_StrLen nOffset = 0 );

// Hilfsklasse zum kopieren von absatzgebundenen Flys. Durch die Sortierung
// nach der Ordnungsnummer wird versucht die layout seitige Anordnung
// bei zu behalten
class _ZSortFly
{
    const SwFrmFmt* pFmt;
    const SwFmtAnchor* pAnchor;
    UINT32 nOrdNum;

public:
    _ZSortFly( const SwFrmFmt* pFrmFmt, const SwFmtAnchor* pFlyAnchor,
                UINT32 nArrOrdNum );
    _ZSortFly& operator=( const _ZSortFly& rCpy )
    {
        pFmt = rCpy.pFmt, pAnchor = rCpy.pAnchor, nOrdNum = rCpy.nOrdNum;
        return *this;
    }

    int operator==( const _ZSortFly& ) const { return FALSE; }
    int operator<( const _ZSortFly& rCmp ) const
        { return nOrdNum < rCmp.nOrdNum; }

    const SwFrmFmt* GetFmt() const              { return pFmt; }
    const SwFmtAnchor* GetAnchor() const        { return pAnchor; }
};

SV_DECL_VARARR_SORT( _ZSortFlys, _ZSortFly, 0, 10 )

class SwTblNumFmtMerge
{
    SvNumberFormatter* pNFmt;
public:
    SwTblNumFmtMerge( const SwDoc& rSrc, SwDoc& rDest );
    ~SwTblNumFmtMerge();
};

class _SaveRedlEndPosForRestore
{
    SvPtrarr* pSavArr;
    SwNodeIndex* pSavIdx;
    xub_StrLen nSavCntnt;

    void _Restore();
public:
    _SaveRedlEndPosForRestore( const SwNodeIndex& rInsIdx, xub_StrLen nCntnt );
    ~_SaveRedlEndPosForRestore();
    void Restore() { if( pSavArr ) _Restore(); }
};

#endif  // _MVSAVE_HXX

<|endoftext|>


<commit_before>// Copyright 2013 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 "content/browser/tracing/tracing_controller_impl.h"

#include "base/bind.h"
#include "base/file_util.h"
#include "base/json/string_escape.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/tracing/trace_message_filter.h"
#include "content/common/child_process_messages.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/common/content_switches.h"

using base::debug::TraceLog;

namespace content {

namespace {

base::LazyInstance<TracingControllerImpl>::Leaky g_controller =
    LAZY_INSTANCE_INITIALIZER;

}  // namespace

TracingController* TracingController::GetInstance() {
  return TracingControllerImpl::GetInstance();
}

TracingControllerImpl::TracingControllerImpl() :
    pending_disable_recording_ack_count_(0),
    pending_capture_monitoring_snapshot_ack_count_(0),
    is_recording_(false),
    is_monitoring_(false),
    category_filter_(
        base::debug::CategoryFilter::kDefaultCategoryFilterString),
    result_file_(0),
    result_file_has_at_least_one_result_(false) {
}

TracingControllerImpl::~TracingControllerImpl() {
  // This is a Leaky instance.
  NOTREACHED();
}

TracingControllerImpl* TracingControllerImpl::GetInstance() {
  return g_controller.Pointer();
}

void TracingControllerImpl::GetCategories(
    const GetCategoriesDoneCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Known categories come back from child processes with the EndTracingAck
  // message. So to get known categories, just begin and end tracing immediately
  // afterwards. This will ping all the child processes for categories.
  pending_get_categories_done_callback_ = callback;
  EnableRecording(base::debug::CategoryFilter("*"),
                  TracingController::Options(),
                  EnableRecordingDoneCallback());
  DisableRecording(TracingFileResultCallback());
}

bool TracingControllerImpl::EnableRecording(
    const base::debug::CategoryFilter& filter,
    TracingController::Options options,
    const EnableRecordingDoneCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!can_enable_recording())
    return false;

  trace_options_ = TraceLog::GetInstance()->trace_options();
  TraceLog::GetInstance()->SetEnabled(filter, trace_options_);

  is_recording_ = true;
  category_filter_ = TraceLog::GetInstance()->GetCurrentCategoryFilter();

  // Notify all child processes.
  for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
    it->get()->SendBeginTracing(category_filter_.ToString(), trace_options_);
  }

  if (!callback.is_null())
    callback.Run();
  return true;
}

bool TracingControllerImpl::DisableRecording(
    const TracingFileResultCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!can_disable_recording())
    return false;

  pending_disable_recording_done_callback_ = callback;

  // Disable local trace early to avoid traces during end-tracing process from
  // interfering with the process.
  TraceLog::GetInstance()->SetDisabled();

  // We don't need to create a temporary file when getting categories.
  if (pending_get_categories_done_callback_.is_null()) {
    base::FilePath temporary_file;
    file_util::CreateTemporaryFile(&temporary_file);
    result_file_path_.reset(new base::FilePath(temporary_file));
    result_file_ = file_util::OpenFile(*result_file_path_, "w");
    result_file_has_at_least_one_result_ = false;
    const char* preamble = "{\"traceEvents\": [";
    fwrite(preamble, strlen(preamble), 1, result_file_);
  }

  // There could be a case where there are no child processes and filters_
  // is empty. In that case we can immediately tell the subscriber that tracing
  // has ended. To avoid recursive calls back to the subscriber, we will just
  // use the existing asynchronous OnDisableRecordingAcked code.
  // Count myself (local trace) in pending_disable_recording_ack_count_,
  // acked below.
  pending_disable_recording_ack_count_ = filters_.size() + 1;

  // Handle special case of zero child processes.
  if (pending_disable_recording_ack_count_ == 1) {
    // Ack asynchronously now, because we don't have any children to wait for.
    std::vector<std::string> category_groups;
    TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
                   base::Unretained(this), category_groups));
  }

  // Notify all child processes.
  for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
    it->get()->SendEndTracing();
  }
  return true;
}

bool TracingControllerImpl::EnableMonitoring(
    const base::debug::CategoryFilter& filter,
    TracingController::Options options,
    const EnableMonitoringDoneCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!can_enable_monitoring())
    return false;
  is_monitoring_ = true;

  int monitoring_tracing_options = 0;
  if (options & ENABLE_SAMPLING)
    monitoring_tracing_options |= base::debug::TraceLog::MONITOR_SAMPLING;

  // Notify all child processes.
  for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
    it->get()->SendEnableMonitoring(filter.ToString(),
        base::debug::TraceLog::Options(monitoring_tracing_options));
  }

  if (!callback.is_null())
    callback.Run();
  return true;
}

bool TracingControllerImpl::DisableMonitoring(
    const DisableMonitoringDoneCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!can_disable_monitoring())
    return false;
  is_monitoring_ = false;

  // Notify all child processes.
  for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
    it->get()->SendDisableMonitoring();
  }

  if (!callback.is_null())
    callback.Run();
  return true;
}

void TracingControllerImpl::GetMonitoringStatus(
    bool* out_enabled,
    base::debug::CategoryFilter* out_filter,
    TracingController::Options* out_options) {
  NOTIMPLEMENTED();
}

void TracingControllerImpl::CaptureMonitoringSnapshot(
    const TracingFileResultCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!can_disable_monitoring())
    return;

  pending_capture_monitoring_snapshot_done_callback_ = callback;

  base::FilePath temporary_file;
  file_util::CreateTemporaryFile(&temporary_file);
  result_file_path_.reset(new base::FilePath(temporary_file));
  result_file_ = file_util::OpenFile(*result_file_path_, "w");
  result_file_has_at_least_one_result_ = false;
  const char* preamble = "{\"traceEvents\": [";
  fwrite(preamble, strlen(preamble), 1, result_file_);

  // There could be a case where there are no child processes and filters_
  // is empty. In that case we can immediately tell the subscriber that tracing
  // has ended. To avoid recursive calls back to the subscriber, we will just
  // use the existing asynchronous OnCaptureMonitoringSnapshotAcked code.
  // Count myself in pending_capture_monitoring_snapshot_ack_count_,
  // acked below.
  pending_capture_monitoring_snapshot_ack_count_ = filters_.size() + 1;

  // Handle special case of zero child processes.
  if (pending_capture_monitoring_snapshot_ack_count_ == 1) {
    // Ack asynchronously now, because we don't have any children to wait for.
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
                   base::Unretained(this)));
  }

  // Notify all child processes.
  for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
    it->get()->SendCaptureMonitoringSnapshot();
  }
}

void TracingControllerImpl::AddFilter(TraceMessageFilter* filter) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::AddFilter, base::Unretained(this),
                   make_scoped_refptr(filter)));
    return;
  }

  filters_.insert(filter);
  if (can_disable_recording()) {
    std::string cf_str = category_filter_.ToString();
    filter->SendBeginTracing(cf_str, trace_options_);
  }
}

void TracingControllerImpl::RemoveFilter(TraceMessageFilter* filter) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::RemoveFilter, base::Unretained(this),
                   make_scoped_refptr(filter)));
    return;
  }

  filters_.erase(filter);
}

void TracingControllerImpl::OnDisableRecordingAcked(
    const std::vector<std::string>& known_category_groups) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
                   base::Unretained(this), known_category_groups));
    return;
  }

  // Merge known_category_groups with known_category_groups_
  known_category_groups_.insert(known_category_groups.begin(),
                                known_category_groups.end());

  if (pending_disable_recording_ack_count_ == 0)
    return;

  if (--pending_disable_recording_ack_count_ == 1) {
    // All acks from subprocesses have been received. Now flush the local trace.
    // During or after this call, our OnLocalTraceDataCollected will be
    // called with the last of the local trace data.
    TraceLog::GetInstance()->Flush(
        base::Bind(&TracingControllerImpl::OnLocalTraceDataCollected,
                   base::Unretained(this)));
  }

  if (pending_disable_recording_ack_count_ != 0)
    return;

  // All acks (including from the subprocesses and the local trace) have been
  // received.
  is_recording_ = false;

  // Trigger callback if one is set.
  if (!pending_get_categories_done_callback_.is_null()) {
    pending_get_categories_done_callback_.Run(known_category_groups_);
    pending_get_categories_done_callback_.Reset();
  } else if (!pending_disable_recording_done_callback_.is_null()) {
    const char* trailout = "]}";
    fwrite(trailout, strlen(trailout), 1, result_file_);
    file_util::CloseFile(result_file_);
    result_file_ = 0;
    pending_disable_recording_done_callback_.Run(result_file_path_.Pass());
    pending_disable_recording_done_callback_.Reset();
  }
}

void TracingControllerImpl::OnCaptureMonitoringSnapshotAcked() {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
                   base::Unretained(this)));
    return;
  }

  if (pending_capture_monitoring_snapshot_ack_count_ == 0)
    return;

  if (--pending_capture_monitoring_snapshot_ack_count_ == 1) {
    // All acks from subprocesses have been received. Now flush the local trace.
    // During or after this call, our OnLocalMonitoringTraceDataCollected
    // will be called with the last of the local trace data.
    TraceLog::GetInstance()->FlushButLeaveBufferIntact(
        base::Bind(&TracingControllerImpl::OnLocalMonitoringTraceDataCollected,
                   base::Unretained(this)));
  }

  if (pending_capture_monitoring_snapshot_ack_count_ != 0)
    return;

  if (!pending_capture_monitoring_snapshot_done_callback_.is_null()) {
    const char* trailout = "]}";
    fwrite(trailout, strlen(trailout), 1, result_file_);
    file_util::CloseFile(result_file_);
    result_file_ = 0;
    pending_capture_monitoring_snapshot_done_callback_.Run(
        result_file_path_.Pass());
    pending_capture_monitoring_snapshot_done_callback_.Reset();
  }
}

void TracingControllerImpl::OnTraceDataCollected(
    const scoped_refptr<base::RefCountedString>& events_str_ptr) {
  // OnTraceDataCollected may be called from any browser thread, either by the
  // local event trace system or from child processes via TraceMessageFilter.
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::OnTraceDataCollected,
                   base::Unretained(this), events_str_ptr));
    return;
  }

  // Drop trace events if we are just getting categories.
  if (!pending_get_categories_done_callback_.is_null())
    return;

  // If there is already a result in the file, then put a commma
  // before the next batch of results.
  if (result_file_has_at_least_one_result_) {
    fwrite(",", 1, 1, result_file_);
  } else {
    result_file_has_at_least_one_result_ = true;
  }
  fwrite(events_str_ptr->data().c_str(), events_str_ptr->data().size(), 1,
         result_file_);
}

void TracingControllerImpl::OnLocalTraceDataCollected(
    const scoped_refptr<base::RefCountedString>& events_str_ptr,
    bool has_more_events) {
  if (events_str_ptr->data().size())
    OnTraceDataCollected(events_str_ptr);

  if (has_more_events)
    return;

  // Simulate an DisableRecordingAcked for the local trace.
  std::vector<std::string> category_groups;
  TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
  OnDisableRecordingAcked(category_groups);
}

void TracingControllerImpl::OnLocalMonitoringTraceDataCollected(
    const scoped_refptr<base::RefCountedString>& events_str_ptr,
    bool has_more_events) {
  if (events_str_ptr->data().size())
    OnTraceDataCollected(events_str_ptr);

  if (has_more_events)
    return;

  // Simulate an CaptureMonitoringSnapshotAcked for the local trace.
  OnCaptureMonitoringSnapshotAcked();
}

}  // namespace content
<commit_msg>Fix failures in chrome os after r227262<commit_after>// Copyright 2013 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 "content/browser/tracing/tracing_controller_impl.h"

#include "base/bind.h"
#include "base/file_util.h"
#include "base/json/string_escape.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/tracing/trace_message_filter.h"
#include "content/common/child_process_messages.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/common/content_switches.h"

using base::debug::TraceLog;

namespace content {

namespace {

base::LazyInstance<TracingControllerImpl>::Leaky g_controller =
    LAZY_INSTANCE_INITIALIZER;

}  // namespace

TracingController* TracingController::GetInstance() {
  return TracingControllerImpl::GetInstance();
}

TracingControllerImpl::TracingControllerImpl() :
    pending_disable_recording_ack_count_(0),
    pending_capture_monitoring_snapshot_ack_count_(0),
    is_recording_(false),
    is_monitoring_(false),
    category_filter_(
        base::debug::CategoryFilter::kDefaultCategoryFilterString),
    result_file_(0),
    result_file_has_at_least_one_result_(false) {
}

TracingControllerImpl::~TracingControllerImpl() {
  // This is a Leaky instance.
  NOTREACHED();
}

TracingControllerImpl* TracingControllerImpl::GetInstance() {
  return g_controller.Pointer();
}

void TracingControllerImpl::GetCategories(
    const GetCategoriesDoneCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Known categories come back from child processes with the EndTracingAck
  // message. So to get known categories, just begin and end tracing immediately
  // afterwards. This will ping all the child processes for categories.
  pending_get_categories_done_callback_ = callback;
  EnableRecording(base::debug::CategoryFilter("*"),
                  TracingController::Options(),
                  EnableRecordingDoneCallback());
  DisableRecording(TracingFileResultCallback());
}

bool TracingControllerImpl::EnableRecording(
    const base::debug::CategoryFilter& filter,
    TracingController::Options options,
    const EnableRecordingDoneCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!can_enable_recording())
    return false;

  trace_options_ = TraceLog::GetInstance()->trace_options();
  TraceLog::GetInstance()->SetEnabled(filter, trace_options_);

  is_recording_ = true;
  category_filter_ = TraceLog::GetInstance()->GetCurrentCategoryFilter();

  // Notify all child processes.
  for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
    it->get()->SendBeginTracing(category_filter_.ToString(), trace_options_);
  }

  if (!callback.is_null())
    callback.Run();
  return true;
}

bool TracingControllerImpl::DisableRecording(
    const TracingFileResultCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!can_disable_recording())
    return false;

  pending_disable_recording_done_callback_ = callback;

  // Disable local trace early to avoid traces during end-tracing process from
  // interfering with the process.
  TraceLog::GetInstance()->SetDisabled();

  // We don't need to create a temporary file when getting categories.
  if (pending_get_categories_done_callback_.is_null()) {
    base::FilePath temporary_file;
    file_util::CreateTemporaryFile(&temporary_file);
    result_file_path_.reset(new base::FilePath(temporary_file));
    result_file_ = file_util::OpenFile(*result_file_path_, "w");
    result_file_has_at_least_one_result_ = false;
    const char* preamble = "{\"traceEvents\": [";
    size_t written_bytes = fwrite(preamble, strlen(preamble), 1, result_file_);
    DCHECK(written_bytes == strlen(preamble));
  }

  // There could be a case where there are no child processes and filters_
  // is empty. In that case we can immediately tell the subscriber that tracing
  // has ended. To avoid recursive calls back to the subscriber, we will just
  // use the existing asynchronous OnDisableRecordingAcked code.
  // Count myself (local trace) in pending_disable_recording_ack_count_,
  // acked below.
  pending_disable_recording_ack_count_ = filters_.size() + 1;

  // Handle special case of zero child processes.
  if (pending_disable_recording_ack_count_ == 1) {
    // Ack asynchronously now, because we don't have any children to wait for.
    std::vector<std::string> category_groups;
    TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
                   base::Unretained(this), category_groups));
  }

  // Notify all child processes.
  for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
    it->get()->SendEndTracing();
  }
  return true;
}

bool TracingControllerImpl::EnableMonitoring(
    const base::debug::CategoryFilter& filter,
    TracingController::Options options,
    const EnableMonitoringDoneCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!can_enable_monitoring())
    return false;
  is_monitoring_ = true;

  int monitoring_tracing_options = 0;
  if (options & ENABLE_SAMPLING)
    monitoring_tracing_options |= base::debug::TraceLog::MONITOR_SAMPLING;

  // Notify all child processes.
  for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
    it->get()->SendEnableMonitoring(filter.ToString(),
        base::debug::TraceLog::Options(monitoring_tracing_options));
  }

  if (!callback.is_null())
    callback.Run();
  return true;
}

bool TracingControllerImpl::DisableMonitoring(
    const DisableMonitoringDoneCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!can_disable_monitoring())
    return false;
  is_monitoring_ = false;

  // Notify all child processes.
  for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
    it->get()->SendDisableMonitoring();
  }

  if (!callback.is_null())
    callback.Run();
  return true;
}

void TracingControllerImpl::GetMonitoringStatus(
    bool* out_enabled,
    base::debug::CategoryFilter* out_filter,
    TracingController::Options* out_options) {
  NOTIMPLEMENTED();
}

void TracingControllerImpl::CaptureMonitoringSnapshot(
    const TracingFileResultCallback& callback) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!can_disable_monitoring())
    return;

  pending_capture_monitoring_snapshot_done_callback_ = callback;

  base::FilePath temporary_file;
  file_util::CreateTemporaryFile(&temporary_file);
  result_file_path_.reset(new base::FilePath(temporary_file));
  result_file_ = file_util::OpenFile(*result_file_path_, "w");
  result_file_has_at_least_one_result_ = false;
  const char* preamble = "{\"traceEvents\": [";
  size_t written_bytes = fwrite(preamble, strlen(preamble), 1, result_file_);
  DCHECK(written_bytes == strlen(preamble));

  // There could be a case where there are no child processes and filters_
  // is empty. In that case we can immediately tell the subscriber that tracing
  // has ended. To avoid recursive calls back to the subscriber, we will just
  // use the existing asynchronous OnCaptureMonitoringSnapshotAcked code.
  // Count myself in pending_capture_monitoring_snapshot_ack_count_,
  // acked below.
  pending_capture_monitoring_snapshot_ack_count_ = filters_.size() + 1;

  // Handle special case of zero child processes.
  if (pending_capture_monitoring_snapshot_ack_count_ == 1) {
    // Ack asynchronously now, because we don't have any children to wait for.
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
                   base::Unretained(this)));
  }

  // Notify all child processes.
  for (FilterMap::iterator it = filters_.begin(); it != filters_.end(); ++it) {
    it->get()->SendCaptureMonitoringSnapshot();
  }
}

void TracingControllerImpl::AddFilter(TraceMessageFilter* filter) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::AddFilter, base::Unretained(this),
                   make_scoped_refptr(filter)));
    return;
  }

  filters_.insert(filter);
  if (can_disable_recording()) {
    std::string cf_str = category_filter_.ToString();
    filter->SendBeginTracing(cf_str, trace_options_);
  }
}

void TracingControllerImpl::RemoveFilter(TraceMessageFilter* filter) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::RemoveFilter, base::Unretained(this),
                   make_scoped_refptr(filter)));
    return;
  }

  filters_.erase(filter);
}

void TracingControllerImpl::OnDisableRecordingAcked(
    const std::vector<std::string>& known_category_groups) {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::OnDisableRecordingAcked,
                   base::Unretained(this), known_category_groups));
    return;
  }

  // Merge known_category_groups with known_category_groups_
  known_category_groups_.insert(known_category_groups.begin(),
                                known_category_groups.end());

  if (pending_disable_recording_ack_count_ == 0)
    return;

  if (--pending_disable_recording_ack_count_ == 1) {
    // All acks from subprocesses have been received. Now flush the local trace.
    // During or after this call, our OnLocalTraceDataCollected will be
    // called with the last of the local trace data.
    TraceLog::GetInstance()->Flush(
        base::Bind(&TracingControllerImpl::OnLocalTraceDataCollected,
                   base::Unretained(this)));
  }

  if (pending_disable_recording_ack_count_ != 0)
    return;

  // All acks (including from the subprocesses and the local trace) have been
  // received.
  is_recording_ = false;

  // Trigger callback if one is set.
  if (!pending_get_categories_done_callback_.is_null()) {
    pending_get_categories_done_callback_.Run(known_category_groups_);
    pending_get_categories_done_callback_.Reset();
  } else if (!pending_disable_recording_done_callback_.is_null()) {
    const char* trailout = "]}";
    size_t written_bytes = fwrite(trailout, strlen(trailout), 1, result_file_);
    DCHECK(written_bytes == strlen(trailout));
    file_util::CloseFile(result_file_);
    result_file_ = 0;
    pending_disable_recording_done_callback_.Run(result_file_path_.Pass());
    pending_disable_recording_done_callback_.Reset();
  }
}

void TracingControllerImpl::OnCaptureMonitoringSnapshotAcked() {
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::OnCaptureMonitoringSnapshotAcked,
                   base::Unretained(this)));
    return;
  }

  if (pending_capture_monitoring_snapshot_ack_count_ == 0)
    return;

  if (--pending_capture_monitoring_snapshot_ack_count_ == 1) {
    // All acks from subprocesses have been received. Now flush the local trace.
    // During or after this call, our OnLocalMonitoringTraceDataCollected
    // will be called with the last of the local trace data.
    TraceLog::GetInstance()->FlushButLeaveBufferIntact(
        base::Bind(&TracingControllerImpl::OnLocalMonitoringTraceDataCollected,
                   base::Unretained(this)));
  }

  if (pending_capture_monitoring_snapshot_ack_count_ != 0)
    return;

  if (!pending_capture_monitoring_snapshot_done_callback_.is_null()) {
    const char* trailout = "]}";
    size_t written_bytes = fwrite(trailout, strlen(trailout), 1, result_file_);
    DCHECK(written_bytes == strlen(trailout));
    file_util::CloseFile(result_file_);
    result_file_ = 0;
    pending_capture_monitoring_snapshot_done_callback_.Run(
        result_file_path_.Pass());
    pending_capture_monitoring_snapshot_done_callback_.Reset();
  }
}

void TracingControllerImpl::OnTraceDataCollected(
    const scoped_refptr<base::RefCountedString>& events_str_ptr) {
  // OnTraceDataCollected may be called from any browser thread, either by the
  // local event trace system or from child processes via TraceMessageFilter.
  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&TracingControllerImpl::OnTraceDataCollected,
                   base::Unretained(this), events_str_ptr));
    return;
  }

  // Drop trace events if we are just getting categories.
  if (!pending_get_categories_done_callback_.is_null())
    return;

  // If there is already a result in the file, then put a commma
  // before the next batch of results.
  if (result_file_has_at_least_one_result_) {
    size_t written_bytes = fwrite(",", 1, 1, result_file_);
    DCHECK(written_bytes == 1);
  } else {
    result_file_has_at_least_one_result_ = true;
  }
  size_t written_bytes = fwrite(events_str_ptr->data().c_str(),
                                events_str_ptr->data().size(), 1,
                                result_file_);
  DCHECK(written_bytes == events_str_ptr->data().size());
}

void TracingControllerImpl::OnLocalTraceDataCollected(
    const scoped_refptr<base::RefCountedString>& events_str_ptr,
    bool has_more_events) {
  if (events_str_ptr->data().size())
    OnTraceDataCollected(events_str_ptr);

  if (has_more_events)
    return;

  // Simulate an DisableRecordingAcked for the local trace.
  std::vector<std::string> category_groups;
  TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
  OnDisableRecordingAcked(category_groups);
}

void TracingControllerImpl::OnLocalMonitoringTraceDataCollected(
    const scoped_refptr<base::RefCountedString>& events_str_ptr,
    bool has_more_events) {
  if (events_str_ptr->data().size())
    OnTraceDataCollected(events_str_ptr);

  if (has_more_events)
    return;

  // Simulate an CaptureMonitoringSnapshotAcked for the local trace.
  OnCaptureMonitoringSnapshotAcked();
}

}  // namespace content
<|endoftext|>


<commit_before>#include "SyncOutputObject.h"

#include <maya/MFloatArray.h>
#include <maya/MGlobal.h>
#include <maya/MPlug.h>

#include <maya/MFnDependencyNode.h>
#include <maya/MFnDagNode.h>
#include <maya/MDagPath.h>

#include "AssetNode.h"
#include "SyncOutputGeometryPart.h"
#include "SyncOutputInstance.h"

#if MAYA_API_VERSION >= 201400
        #include <maya/MFnFloatArrayData.h>
#endif

SyncOutputObject::SyncOutputObject(
        const MPlug &outputPlug,
        const MObject &assetNodeObj,
        const bool visible,
        const bool syncTemplatedGeos)
    : myOutputPlug(outputPlug),
      myAssetNodeObj(assetNodeObj),
      myVisible(visible),
      mySyncTemplatedGeos(syncTemplatedGeos)
{}

SyncOutputObject::~SyncOutputObject()
{
    for(AssetSyncs::const_iterator it = myAssetSyncs.begin();
            it != myAssetSyncs.end();
            it++)
    {
        delete *it;
    }
    myAssetSyncs.clear();
}

MStatus
SyncOutputObject::doIt()
{
    MStatus status;
    // Create our parts.
    // An object just contains a number of parts, and no
    // other information.
    MFnDependencyNode assetNodeFn(myAssetNodeObj, &status);

    MObject objectTransform = myDagModifier.createNode("transform", myAssetNodeObj, &status);
    CHECK_MSTATUS_AND_RETURN_IT(status);

    // rename objectTransform
    MPlug objectNamePlug = myOutputPlug.child(AssetNode::outputObjectName);

    MString objectName = objectNamePlug.asString();
    if(objectName.length())
    {
        objectName = Util::sanitizeStringForNodeName(objectName);
    }
    else
    {
        objectName = "emptyObject";
    }
    status = myDagModifier.renameNode(objectTransform, objectName);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    CHECK_MSTATUS_AND_RETURN_IT(myDagModifier.doIt());

    MFnDependencyNode objectTransformFn(objectTransform);

    // connect objectTransform attributes
    {
        MPlug transformPlug = myOutputPlug.child(AssetNode::outputObjectTransform);

        MPlug srcPlug;
        MPlug dstPlug;

        srcPlug = transformPlug.child(AssetNode::outputObjectTranslate);
        dstPlug = objectTransformFn.findPlug("translate");
        status = myDagModifier.connect(srcPlug, dstPlug);
        CHECK_MSTATUS_AND_RETURN_IT(status);

        srcPlug = transformPlug.child(AssetNode::outputObjectRotate);
        dstPlug = objectTransformFn.findPlug("rotate");
        status = myDagModifier.connect(srcPlug, dstPlug);
        CHECK_MSTATUS_AND_RETURN_IT(status);

        srcPlug = transformPlug.child(AssetNode::outputObjectScale);
        dstPlug = objectTransformFn.findPlug("scale");
        status = myDagModifier.connect(srcPlug, dstPlug);
        CHECK_MSTATUS_AND_RETURN_IT(status);

        CHECK_MSTATUS_AND_RETURN_IT(myDagModifier.doIt());
    }

    if(!myVisible)
    {
        status = myDagModifier.newPlugValueBool(
                objectTransformFn.findPlug("visibility"),
                false
                );
        CHECK_MSTATUS_AND_RETURN_IT(status);
    }

    MPlug geosPlug = myOutputPlug.child(AssetNode::outputGeos);
    int geoCount = geosPlug.evaluateNumElements(&status);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    for(int ii = 0; ii < geoCount; ii++)
    {
        MPlug geoPlug = geosPlug[ii];

        MObject geoTransform = MObject::kNullObj;
        MObject partParent = objectTransform;

        MPlug isTemplatedPlug = geoPlug.child(AssetNode::outputGeoIsTemplated);
        MPlug isDisplayGeoPlug = geoPlug.child(AssetNode::outputGeoIsDisplayGeo);
        if(mySyncTemplatedGeos || !isTemplatedPlug.asBool() || isDisplayGeoPlug.asBool())
        {
            if(geoCount > 1)
            {
                geoTransform = myDagModifier.createNode("transform", objectTransform, &status);
                CHECK_MSTATUS_AND_RETURN_IT(status);

                // rename geoTransform
                MPlug geoNamePlug = geoPlug.child(AssetNode::outputGeoName);
                MString geoName = geoNamePlug.asString();
                if(geoName.length())
                {
                    geoName = Util::sanitizeStringForNodeName(geoName);
                }
                else
                {
                    geoName = "emptyGeo";
                }
                status = myDagModifier.renameNode(geoTransform, geoName);
                CHECK_MSTATUS_AND_RETURN_IT(status);
                CHECK_MSTATUS_AND_RETURN_IT(myDagModifier.doIt());

                partParent = geoTransform;
            }

            MPlug partsPlug = geoPlug.child(AssetNode::outputParts);
            int partCount = partsPlug.evaluateNumElements(&status);
            CHECK_MSTATUS_AND_RETURN_IT(status);
            for(int jj=0; jj<partCount; jj++)
            {
                SubCommand* sync = new SyncOutputGeometryPart(partsPlug[jj], partParent);
                sync->doIt();
                myAssetSyncs.push_back(sync);
            }
        }
    }

#if MAYA_API_VERSION >= 201400
    createFluidShape(objectTransform);
#endif

    return MStatus::kSuccess;
}

#if MAYA_API_VERSION >= 201400
MStatus
SyncOutputObject::createVelocityConverter(MObject& velocityConverter)
{
    if(!velocityConverter.isNull())
        return MS::kSuccess;

    MStatus status;
    velocityConverter = ((MDGModifier&)myDagModifier).createNode("houdiniFluidVelocityConvert", &status);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    return status;
}

bool
SyncOutputObject::resolutionsEqual(MPlug resA, MPlug resB)
{
    MPlug resAPlug = resA.child(AssetNode::outputPartVolumeRes);
    MObject resAObj;
    resAPlug.getValue(resAObj);

    MPlug resBPlug = resB.child(AssetNode::outputPartVolumeRes);
    MObject resBObj;
    resBPlug.getValue(resBObj);

    MStatus status;
    MFnFloatArrayData dataA(resAObj, &status);
    MFnFloatArrayData dataB(resBObj, &status);

    return dataA[0] == dataB[0] &&
           dataA[1] == dataB[1] &&
           dataA[2] == dataB[2];
}

MStatus
SyncOutputObject::createFluidShape(const MObject &objectTransform)
{
    MStatus status;

    MPlug geosPlug = myOutputPlug.child(AssetNode::outputGeos);
    int geoCount = geosPlug.evaluateNumElements(&status);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    for(int ii = 0; ii < geoCount; ii++)
    {
        MPlug geoPlug = geosPlug[ii];

        MPlug partsPlug = geoPlug.child(AssetNode::outputParts);
        int partCount = partsPlug.evaluateNumElements(&status);
        CHECK_MSTATUS_AND_RETURN_IT(status);

        // Look for specific volume names. Once we've found the first reference
        // volume, look again through everything for any volumes which share a
        // transform with the first reference volume, and add them to the
        // fluidShape.
        bool hasFluid = false;
        MPlug referenceVolume;
        for(int jj=0; jj<partCount; jj++)
        {
            MPlug outputVolume = partsPlug[jj].child(AssetNode::outputPartVolume);
            MPlug outputPartName = partsPlug[jj].child(AssetNode::outputPartName);
            MPlug outputVolumeName = outputVolume.child(AssetNode::outputPartVolumeName);

            MString name = outputVolumeName.asString();

            if(name == "density"
                    || name == "temperature"
                    || name == "fuel"
                    || name == "vel.x"
                    || name == "vel.y"
                    || name == "vel.z")
            {
                hasFluid = true;
                referenceVolume = outputVolume;
                break;
            }
        }

        if(!hasFluid)
            return MStatus::kSuccess;

        MObject transform, fluid;
        {
            transform = myDagModifier.createNode("transform", objectTransform, &status);
            CHECK_MSTATUS_AND_RETURN_IT(status);
            // TODO: name
            status  = myDagModifier.renameNode(transform, "fluid_transform");
            CHECK_MSTATUS_AND_RETURN_IT(status);

            fluid = myDagModifier.createNode("fluidShape", transform, &status);
            CHECK_MSTATUS_AND_RETURN_IT(status);
        }

        MPlug referenceRes = referenceVolume.child(AssetNode::outputPartVolumeRes);

        MFnDagNode partVolumeFn(fluid, &status);
        CHECK_MSTATUS_AND_RETURN_IT(status);
        MFnDagNode partFluidTransformFn(transform, &status);
        CHECK_MSTATUS_AND_RETURN_IT(status);

        MObject velConverter;

        bool doneDensity = false;
        bool doneTemperature = false;
        bool doneFuel = false;
        bool doneVelX = false;
        bool doneVelY = false;
        bool doneVelZ = false;
        for(int jj=0; jj<partCount; jj++)
        {
            MPlug outputVolume = partsPlug[jj].child(AssetNode::outputPartVolume);
            MPlug outputVolumeName = outputVolume.child(AssetNode::outputPartVolumeName);
            MPlug outputVolumeRes= outputVolume.child(AssetNode::outputPartVolumeRes);

            // If the transform of the volumes are different, we don't want
            // to group them together.
            if(!resolutionsEqual(outputVolumeRes, referenceRes))
                continue;

            MPlug srcPlug = outputVolume.child(AssetNode::outputPartVolumeGrid);
            MString name = outputVolumeName.asString();
            if(name == "density" && !doneDensity)
            {
                status = myDagModifier.connect(srcPlug, partVolumeFn.findPlug("inDensity"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.newPlugValueInt(
                        partVolumeFn.findPlug("densityMethod"),
                        2
                        );
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneDensity = true;
            }
            else if(name == "temperature" && !doneTemperature)
            {
                status = myDagModifier.connect(srcPlug, partVolumeFn.findPlug("inTemperature"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.newPlugValueInt(
                        partVolumeFn.findPlug("temperatureMethod"),
                        2
                        );
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneTemperature = true;
            }
            else if(name == "fuel" && !doneFuel)
            {
                status = myDagModifier.connect(srcPlug, partVolumeFn.findPlug("inReaction"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.newPlugValueInt(
                        partVolumeFn.findPlug("fuelMethod"),
                        2
                        );
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneFuel = true;
            }
            else if(name == "vel.x" && !doneVelX)
            {
                createVelocityConverter(velConverter);
                MFnDependencyNode velConverterFn(velConverter, &status);
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.connect(srcPlug, velConverterFn.findPlug("inGridX"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneVelX = true;
            }
            else if(name == "vel.y" && !doneVelY)
            {
                createVelocityConverter(velConverter);
                MFnDependencyNode velConverterFn(velConverter, &status);
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.connect(srcPlug, velConverterFn.findPlug("inGridY"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneVelY = true;
            }
            else if(name == "vel.z" && !doneVelZ)
            {
                createVelocityConverter(velConverter);
                MFnDependencyNode velConverterFn(velConverter, &status);
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.connect(srcPlug, velConverterFn.findPlug("inGridZ"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneVelZ = true;
            }
        }

        // Connect the transform, resolution, playFromCache; set inOffset and dimensions
        {
            MPlug srcPlug;
            MPlug dstPlug;

            MPlug densityTransform = referenceVolume.child(AssetNode::outputPartVolumeTransform);

            srcPlug = densityTransform.child(AssetNode::outputPartVolumeTranslate);
            dstPlug = partFluidTransformFn.findPlug("translate");
            status = myDagModifier.connect(srcPlug, dstPlug);
            CHECK_MSTATUS_AND_RETURN_IT(status);

            srcPlug = referenceVolume.child(AssetNode::outputPartVolumeRes);
            dstPlug = partVolumeFn.findPlug("inResolution");
            status = myDagModifier.connect(srcPlug, dstPlug);
            CHECK_MSTATUS_AND_RETURN_IT(status);

            srcPlug = densityTransform.child(AssetNode::outputPartVolumeRotate);
            dstPlug = partFluidTransformFn.findPlug("rotate");
            status = myDagModifier.connect(srcPlug, dstPlug);
            CHECK_MSTATUS_AND_RETURN_IT(status);

            srcPlug = densityTransform.child(AssetNode::outputPartVolumeScale);
            dstPlug = partFluidTransformFn.findPlug("scale");
            status = myDagModifier.connect(srcPlug, dstPlug);
            CHECK_MSTATUS_AND_RETURN_IT(status);

            // Velocity needs an additional step: since houdini may output
            // individual grids for each component, we use a dependency node
            // to append and extrapolate the components
            if(!velConverter.isNull())
            {
                MFnDependencyNode velConverterFn(velConverter, &status);

                srcPlug = referenceVolume.child(AssetNode::outputPartVolumeRes);
                dstPlug = velConverterFn.findPlug("resolution");
                status = myDagModifier.connect(srcPlug, dstPlug);
                CHECK_MSTATUS_AND_RETURN_IT(status);

                srcPlug = velConverterFn.findPlug("outGrid");
                dstPlug = partVolumeFn.findPlug("inVelocity");
                status = myDagModifier.connect(srcPlug, dstPlug);
                CHECK_MSTATUS_AND_RETURN_IT(status);
            }

            // time1.outTime -> fluidShape.currentTime
            {
                MObject srcNode = Util::findNodeByName("time1");
                srcPlug = MFnDependencyNode(srcNode).findPlug("outTime");
                dstPlug = partVolumeFn.findPlug("currentTime");
                status = myDagModifier.connect(srcPlug, dstPlug);
                CHECK_MSTATUS_AND_RETURN_IT(status);
            }

            srcPlug = myOutputPlug.child(AssetNode::outputObjectFluidFromAsset);
            dstPlug = partVolumeFn.findPlug("playFromCache");
            status = myDagModifier.connect(srcPlug, dstPlug);
            CHECK_MSTATUS_AND_RETURN_IT(status);

            // inOffset must be initialized or bugs will be had; and the math is
            // simpler with dimensions all equal to 1.
            MFloatArray offset;
            offset.append(0);
            offset.append(0);
            offset.append(0);
            MFnFloatArrayData offsetCreator;
            MObject data = offsetCreator.create(offset);
            partVolumeFn.findPlug("inOffset").setValue(data);
            partVolumeFn.findPlug("dimensionsW").setValue(1);
            partVolumeFn.findPlug("dimensionsH").setValue(1);
            partVolumeFn.findPlug("dimensionsD").setValue(1);
        }

        // doIt so that we can access the fullPathName
        status = myDagModifier.doIt();
        CHECK_MSTATUS_AND_RETURN_IT(status);

        // assign shader
        status = myDagModifier.commandToExecute(
                "assignSG " + partVolumeFn.fullPathName() + " " + partVolumeFn.fullPathName()
                );
        CHECK_MSTATUS_AND_RETURN_IT(status);
    }

    status = myDagModifier.doIt();
    CHECK_MSTATUS_AND_RETURN_IT(status);

    return MStatus::kSuccess;
}
#endif

MStatus
SyncOutputObject::undoIt()
{
    for(AssetSyncs::reverse_iterator iter = myAssetSyncs.rbegin();
            iter != myAssetSyncs.rend();
            iter++)
    {
        (*iter)->undoIt();
    }
    myDagModifier.undoIt();

    return MStatus::kSuccess;
}

MStatus
SyncOutputObject::redoIt()
{
    myDagModifier.doIt();
    for(AssetSyncs::iterator iter = myAssetSyncs.begin();
            iter != myAssetSyncs.end();
            iter++)
    {
        (*iter)->redoIt();
    }

    return MStatus::kSuccess;
}

bool
SyncOutputObject::isUndoable() const
{
    return true;
}
<commit_msg>Change the fluid transform's name to "fluid"<commit_after>#include "SyncOutputObject.h"

#include <maya/MFloatArray.h>
#include <maya/MGlobal.h>
#include <maya/MPlug.h>

#include <maya/MFnDependencyNode.h>
#include <maya/MFnDagNode.h>
#include <maya/MDagPath.h>

#include "AssetNode.h"
#include "SyncOutputGeometryPart.h"
#include "SyncOutputInstance.h"

#if MAYA_API_VERSION >= 201400
        #include <maya/MFnFloatArrayData.h>
#endif

SyncOutputObject::SyncOutputObject(
        const MPlug &outputPlug,
        const MObject &assetNodeObj,
        const bool visible,
        const bool syncTemplatedGeos)
    : myOutputPlug(outputPlug),
      myAssetNodeObj(assetNodeObj),
      myVisible(visible),
      mySyncTemplatedGeos(syncTemplatedGeos)
{}

SyncOutputObject::~SyncOutputObject()
{
    for(AssetSyncs::const_iterator it = myAssetSyncs.begin();
            it != myAssetSyncs.end();
            it++)
    {
        delete *it;
    }
    myAssetSyncs.clear();
}

MStatus
SyncOutputObject::doIt()
{
    MStatus status;
    // Create our parts.
    // An object just contains a number of parts, and no
    // other information.
    MFnDependencyNode assetNodeFn(myAssetNodeObj, &status);

    MObject objectTransform = myDagModifier.createNode("transform", myAssetNodeObj, &status);
    CHECK_MSTATUS_AND_RETURN_IT(status);

    // rename objectTransform
    MPlug objectNamePlug = myOutputPlug.child(AssetNode::outputObjectName);

    MString objectName = objectNamePlug.asString();
    if(objectName.length())
    {
        objectName = Util::sanitizeStringForNodeName(objectName);
    }
    else
    {
        objectName = "emptyObject";
    }
    status = myDagModifier.renameNode(objectTransform, objectName);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    CHECK_MSTATUS_AND_RETURN_IT(myDagModifier.doIt());

    MFnDependencyNode objectTransformFn(objectTransform);

    // connect objectTransform attributes
    {
        MPlug transformPlug = myOutputPlug.child(AssetNode::outputObjectTransform);

        MPlug srcPlug;
        MPlug dstPlug;

        srcPlug = transformPlug.child(AssetNode::outputObjectTranslate);
        dstPlug = objectTransformFn.findPlug("translate");
        status = myDagModifier.connect(srcPlug, dstPlug);
        CHECK_MSTATUS_AND_RETURN_IT(status);

        srcPlug = transformPlug.child(AssetNode::outputObjectRotate);
        dstPlug = objectTransformFn.findPlug("rotate");
        status = myDagModifier.connect(srcPlug, dstPlug);
        CHECK_MSTATUS_AND_RETURN_IT(status);

        srcPlug = transformPlug.child(AssetNode::outputObjectScale);
        dstPlug = objectTransformFn.findPlug("scale");
        status = myDagModifier.connect(srcPlug, dstPlug);
        CHECK_MSTATUS_AND_RETURN_IT(status);

        CHECK_MSTATUS_AND_RETURN_IT(myDagModifier.doIt());
    }

    if(!myVisible)
    {
        status = myDagModifier.newPlugValueBool(
                objectTransformFn.findPlug("visibility"),
                false
                );
        CHECK_MSTATUS_AND_RETURN_IT(status);
    }

    MPlug geosPlug = myOutputPlug.child(AssetNode::outputGeos);
    int geoCount = geosPlug.evaluateNumElements(&status);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    for(int ii = 0; ii < geoCount; ii++)
    {
        MPlug geoPlug = geosPlug[ii];

        MObject geoTransform = MObject::kNullObj;
        MObject partParent = objectTransform;

        MPlug isTemplatedPlug = geoPlug.child(AssetNode::outputGeoIsTemplated);
        MPlug isDisplayGeoPlug = geoPlug.child(AssetNode::outputGeoIsDisplayGeo);
        if(mySyncTemplatedGeos || !isTemplatedPlug.asBool() || isDisplayGeoPlug.asBool())
        {
            if(geoCount > 1)
            {
                geoTransform = myDagModifier.createNode("transform", objectTransform, &status);
                CHECK_MSTATUS_AND_RETURN_IT(status);

                // rename geoTransform
                MPlug geoNamePlug = geoPlug.child(AssetNode::outputGeoName);
                MString geoName = geoNamePlug.asString();
                if(geoName.length())
                {
                    geoName = Util::sanitizeStringForNodeName(geoName);
                }
                else
                {
                    geoName = "emptyGeo";
                }
                status = myDagModifier.renameNode(geoTransform, geoName);
                CHECK_MSTATUS_AND_RETURN_IT(status);
                CHECK_MSTATUS_AND_RETURN_IT(myDagModifier.doIt());

                partParent = geoTransform;
            }

            MPlug partsPlug = geoPlug.child(AssetNode::outputParts);
            int partCount = partsPlug.evaluateNumElements(&status);
            CHECK_MSTATUS_AND_RETURN_IT(status);
            for(int jj=0; jj<partCount; jj++)
            {
                SubCommand* sync = new SyncOutputGeometryPart(partsPlug[jj], partParent);
                sync->doIt();
                myAssetSyncs.push_back(sync);
            }
        }
    }

#if MAYA_API_VERSION >= 201400
    createFluidShape(objectTransform);
#endif

    return MStatus::kSuccess;
}

#if MAYA_API_VERSION >= 201400
MStatus
SyncOutputObject::createVelocityConverter(MObject& velocityConverter)
{
    if(!velocityConverter.isNull())
        return MS::kSuccess;

    MStatus status;
    velocityConverter = ((MDGModifier&)myDagModifier).createNode("houdiniFluidVelocityConvert", &status);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    return status;
}

bool
SyncOutputObject::resolutionsEqual(MPlug resA, MPlug resB)
{
    MPlug resAPlug = resA.child(AssetNode::outputPartVolumeRes);
    MObject resAObj;
    resAPlug.getValue(resAObj);

    MPlug resBPlug = resB.child(AssetNode::outputPartVolumeRes);
    MObject resBObj;
    resBPlug.getValue(resBObj);

    MStatus status;
    MFnFloatArrayData dataA(resAObj, &status);
    MFnFloatArrayData dataB(resBObj, &status);

    return dataA[0] == dataB[0] &&
           dataA[1] == dataB[1] &&
           dataA[2] == dataB[2];
}

MStatus
SyncOutputObject::createFluidShape(const MObject &objectTransform)
{
    MStatus status;

    MPlug geosPlug = myOutputPlug.child(AssetNode::outputGeos);
    int geoCount = geosPlug.evaluateNumElements(&status);
    CHECK_MSTATUS_AND_RETURN_IT(status);
    for(int ii = 0; ii < geoCount; ii++)
    {
        MPlug geoPlug = geosPlug[ii];

        MPlug partsPlug = geoPlug.child(AssetNode::outputParts);
        int partCount = partsPlug.evaluateNumElements(&status);
        CHECK_MSTATUS_AND_RETURN_IT(status);

        // Look for specific volume names. Once we've found the first reference
        // volume, look again through everything for any volumes which share a
        // transform with the first reference volume, and add them to the
        // fluidShape.
        bool hasFluid = false;
        MPlug referenceVolume;
        for(int jj=0; jj<partCount; jj++)
        {
            MPlug outputVolume = partsPlug[jj].child(AssetNode::outputPartVolume);
            MPlug outputPartName = partsPlug[jj].child(AssetNode::outputPartName);
            MPlug outputVolumeName = outputVolume.child(AssetNode::outputPartVolumeName);

            MString name = outputVolumeName.asString();

            if(name == "density"
                    || name == "temperature"
                    || name == "fuel"
                    || name == "vel.x"
                    || name == "vel.y"
                    || name == "vel.z")
            {
                hasFluid = true;
                referenceVolume = outputVolume;
                break;
            }
        }

        if(!hasFluid)
            return MStatus::kSuccess;

        MObject transform, fluid;
        {
            transform = myDagModifier.createNode("transform", objectTransform, &status);
            CHECK_MSTATUS_AND_RETURN_IT(status);
            // TODO: name
            status  = myDagModifier.renameNode(transform, "fluid");
            CHECK_MSTATUS_AND_RETURN_IT(status);

            fluid = myDagModifier.createNode("fluidShape", transform, &status);
            CHECK_MSTATUS_AND_RETURN_IT(status);
        }

        MPlug referenceRes = referenceVolume.child(AssetNode::outputPartVolumeRes);

        MFnDagNode partVolumeFn(fluid, &status);
        CHECK_MSTATUS_AND_RETURN_IT(status);
        MFnDagNode partFluidTransformFn(transform, &status);
        CHECK_MSTATUS_AND_RETURN_IT(status);

        MObject velConverter;

        bool doneDensity = false;
        bool doneTemperature = false;
        bool doneFuel = false;
        bool doneVelX = false;
        bool doneVelY = false;
        bool doneVelZ = false;
        for(int jj=0; jj<partCount; jj++)
        {
            MPlug outputVolume = partsPlug[jj].child(AssetNode::outputPartVolume);
            MPlug outputVolumeName = outputVolume.child(AssetNode::outputPartVolumeName);
            MPlug outputVolumeRes= outputVolume.child(AssetNode::outputPartVolumeRes);

            // If the transform of the volumes are different, we don't want
            // to group them together.
            if(!resolutionsEqual(outputVolumeRes, referenceRes))
                continue;

            MPlug srcPlug = outputVolume.child(AssetNode::outputPartVolumeGrid);
            MString name = outputVolumeName.asString();
            if(name == "density" && !doneDensity)
            {
                status = myDagModifier.connect(srcPlug, partVolumeFn.findPlug("inDensity"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.newPlugValueInt(
                        partVolumeFn.findPlug("densityMethod"),
                        2
                        );
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneDensity = true;
            }
            else if(name == "temperature" && !doneTemperature)
            {
                status = myDagModifier.connect(srcPlug, partVolumeFn.findPlug("inTemperature"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.newPlugValueInt(
                        partVolumeFn.findPlug("temperatureMethod"),
                        2
                        );
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneTemperature = true;
            }
            else if(name == "fuel" && !doneFuel)
            {
                status = myDagModifier.connect(srcPlug, partVolumeFn.findPlug("inReaction"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.newPlugValueInt(
                        partVolumeFn.findPlug("fuelMethod"),
                        2
                        );
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneFuel = true;
            }
            else if(name == "vel.x" && !doneVelX)
            {
                createVelocityConverter(velConverter);
                MFnDependencyNode velConverterFn(velConverter, &status);
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.connect(srcPlug, velConverterFn.findPlug("inGridX"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneVelX = true;
            }
            else if(name == "vel.y" && !doneVelY)
            {
                createVelocityConverter(velConverter);
                MFnDependencyNode velConverterFn(velConverter, &status);
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.connect(srcPlug, velConverterFn.findPlug("inGridY"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneVelY = true;
            }
            else if(name == "vel.z" && !doneVelZ)
            {
                createVelocityConverter(velConverter);
                MFnDependencyNode velConverterFn(velConverter, &status);
                CHECK_MSTATUS_AND_RETURN_IT(status);
                status = myDagModifier.connect(srcPlug, velConverterFn.findPlug("inGridZ"));
                CHECK_MSTATUS_AND_RETURN_IT(status);
                doneVelZ = true;
            }
        }

        // Connect the transform, resolution, playFromCache; set inOffset and dimensions
        {
            MPlug srcPlug;
            MPlug dstPlug;

            MPlug densityTransform = referenceVolume.child(AssetNode::outputPartVolumeTransform);

            srcPlug = densityTransform.child(AssetNode::outputPartVolumeTranslate);
            dstPlug = partFluidTransformFn.findPlug("translate");
            status = myDagModifier.connect(srcPlug, dstPlug);
            CHECK_MSTATUS_AND_RETURN_IT(status);

            srcPlug = referenceVolume.child(AssetNode::outputPartVolumeRes);
            dstPlug = partVolumeFn.findPlug("inResolution");
            status = myDagModifier.connect(srcPlug, dstPlug);
            CHECK_MSTATUS_AND_RETURN_IT(status);

            srcPlug = densityTransform.child(AssetNode::outputPartVolumeRotate);
            dstPlug = partFluidTransformFn.findPlug("rotate");
            status = myDagModifier.connect(srcPlug, dstPlug);
            CHECK_MSTATUS_AND_RETURN_IT(status);

            srcPlug = densityTransform.child(AssetNode::outputPartVolumeScale);
            dstPlug = partFluidTransformFn.findPlug("scale");
            status = myDagModifier.connect(srcPlug, dstPlug);
            CHECK_MSTATUS_AND_RETURN_IT(status);

            // Velocity needs an additional step: since houdini may output
            // individual grids for each component, we use a dependency node
            // to append and extrapolate the components
            if(!velConverter.isNull())
            {
                MFnDependencyNode velConverterFn(velConverter, &status);

                srcPlug = referenceVolume.child(AssetNode::outputPartVolumeRes);
                dstPlug = velConverterFn.findPlug("resolution");
                status = myDagModifier.connect(srcPlug, dstPlug);
                CHECK_MSTATUS_AND_RETURN_IT(status);

                srcPlug = velConverterFn.findPlug("outGrid");
                dstPlug = partVolumeFn.findPlug("inVelocity");
                status = myDagModifier.connect(srcPlug, dstPlug);
                CHECK_MSTATUS_AND_RETURN_IT(status);
            }

            // time1.outTime -> fluidShape.currentTime
            {
                MObject srcNode = Util::findNodeByName("time1");
                srcPlug = MFnDependencyNode(srcNode).findPlug("outTime");
                dstPlug = partVolumeFn.findPlug("currentTime");
                status = myDagModifier.connect(srcPlug, dstPlug);
                CHECK_MSTATUS_AND_RETURN_IT(status);
            }

            srcPlug = myOutputPlug.child(AssetNode::outputObjectFluidFromAsset);
            dstPlug = partVolumeFn.findPlug("playFromCache");
            status = myDagModifier.connect(srcPlug, dstPlug);
            CHECK_MSTATUS_AND_RETURN_IT(status);

            // inOffset must be initialized or bugs will be had; and the math is
            // simpler with dimensions all equal to 1.
            MFloatArray offset;
            offset.append(0);
            offset.append(0);
            offset.append(0);
            MFnFloatArrayData offsetCreator;
            MObject data = offsetCreator.create(offset);
            partVolumeFn.findPlug("inOffset").setValue(data);
            partVolumeFn.findPlug("dimensionsW").setValue(1);
            partVolumeFn.findPlug("dimensionsH").setValue(1);
            partVolumeFn.findPlug("dimensionsD").setValue(1);
        }

        // doIt so that we can access the fullPathName
        status = myDagModifier.doIt();
        CHECK_MSTATUS_AND_RETURN_IT(status);

        // assign shader
        status = myDagModifier.commandToExecute(
                "assignSG " + partVolumeFn.fullPathName() + " " + partVolumeFn.fullPathName()
                );
        CHECK_MSTATUS_AND_RETURN_IT(status);
    }

    status = myDagModifier.doIt();
    CHECK_MSTATUS_AND_RETURN_IT(status);

    return MStatus::kSuccess;
}
#endif

MStatus
SyncOutputObject::undoIt()
{
    for(AssetSyncs::reverse_iterator iter = myAssetSyncs.rbegin();
            iter != myAssetSyncs.rend();
            iter++)
    {
        (*iter)->undoIt();
    }
    myDagModifier.undoIt();

    return MStatus::kSuccess;
}

MStatus
SyncOutputObject::redoIt()
{
    myDagModifier.doIt();
    for(AssetSyncs::iterator iter = myAssetSyncs.begin();
            iter != myAssetSyncs.end();
            iter++)
    {
        (*iter)->redoIt();
    }

    return MStatus::kSuccess;
}

bool
SyncOutputObject::isUndoable() const
{
    return true;
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////
//
// Utility Library
// Copyright (C) 2012 Chase Warrington (staff@spacechase0.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////

#ifndef UTIL_STRING_HPP
#define UTIL_STRING_HPP

#include <iomanip>
#include <ios>
#include <sstream>
#include <string>
#include <vector>

namespace util
{
	namespace priv
	{
		template< typename... ARGS >
		void applyManips( std::ios& stream, ARGS&&... args );

		template< typename T, typename... ARGS >
		void applyManips( std::ios& stream, T&& t, ARGS&&... args )
		{
			stream << std::forward( t );
			applyManips( stream, std::forward( args )... );
		}

		void applyManips( std::ios& stream )
		{
		}
	}

	std::vector< std::string > tokenize( const std::string& str, const std::string& symbol = "," );

	template< typename T, typename... ARGS >
	T fromString( const std::string& str, ARGS&&... args )
	{
		T tmp;

		std::stringstream ss;
		priv::applyManips( ss, std::forward( args )... );
		ss << str;
		ss >> tmp;

		return tmp;
	}

	template< typename T, typename... ARGS >
	std::string toString( const T& t, ARGS&&... args )
	{
		std::stringstream ss;
		priv::applyManips( ss, std::forward( args )... );
		ss << t;
		return ss.str();
	}

	// from/toStringHex -> from/toString( ..., std::hex )
}

#endif // UTIL_STRING_HPP
<commit_msg>Oops. Probably should have tested for linking errors before committing. :P<commit_after>////////////////////////////////////////////////////////////
//
// Utility Library
// Copyright (C) 2012 Chase Warrington (staff@spacechase0.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////

#ifndef UTIL_STRING_HPP
#define UTIL_STRING_HPP

#include <iomanip>
#include <ios>
#include <sstream>
#include <string>
#include <vector>

namespace util
{
	namespace priv
	{
		template< typename... ARGS >
		void applyManips( std::ios& stream, ARGS&&... args );

		template< typename T, typename... ARGS >
		void applyManips( std::ios& stream, T&& t, ARGS&&... args )
		{
			stream << std::forward( t );
			applyManips( stream, std::forward( args )... );
		}

		inline void applyManips( std::ios& stream )
		{
		}
	}

	std::vector< std::string > tokenize( const std::string& str, const std::string& symbol = "," );

	template< typename T, typename... ARGS >
	T fromString( const std::string& str, ARGS&&... args )
	{
		T tmp;

		std::stringstream ss;
		priv::applyManips( ss, std::forward( args )... );
		ss << str;
		ss >> tmp;

		return tmp;
	}

	template< typename T, typename... ARGS >
	std::string toString( const T& t, ARGS&&... args )
	{
		std::stringstream ss;
		priv::applyManips( ss, std::forward( args )... );
		ss << t;
		return ss.str();
	}

	// from/toStringHex -> from/toString( ..., std::hex )
}

#endif // UTIL_STRING_HPP
<|endoftext|>


<commit_before>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2019  gatecat <gatecat@ds0.me>
 *  Copyright (C) 2021  Symbiflow Authors
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "xdc.h"

#include <string>

#include "context.h"
#include "log.h"

// Include tcl.h late because it messed with #define's and lets them leave the
// scope of the header.
#include <tcl.h>

NEXTPNR_NAMESPACE_BEGIN

static int obj_set_from_any(Tcl_Interp *interp, Tcl_Obj *objPtr) { return TCL_ERROR; }

static void set_tcl_obj_string(Tcl_Obj *objPtr, const std::string &s)
{
    NPNR_ASSERT(objPtr->bytes == nullptr);
    // Need to have space for the end null byte.
    objPtr->bytes = Tcl_Alloc(s.size() + 1);

    // Length is length of string, not including the end null byte.
    objPtr->length = s.size();

    std::copy(s.begin(), s.end(), objPtr->bytes);
    objPtr->bytes[objPtr->length] = '\0';
}

static void port_update_string(Tcl_Obj *objPtr)
{
    const Context *ctx = static_cast<const Context *>(objPtr->internalRep.twoPtrValue.ptr1);
    PortInfo *port_info = static_cast<PortInfo *>(objPtr->internalRep.twoPtrValue.ptr2);

    std::string port_name = port_info->name.str(ctx);
    set_tcl_obj_string(objPtr, port_name);
}

static void cell_update_string(Tcl_Obj *objPtr)
{
    const Context *ctx = static_cast<const Context *>(objPtr->internalRep.twoPtrValue.ptr1);
    CellInfo *cell_info = static_cast<CellInfo *>(objPtr->internalRep.twoPtrValue.ptr2);

    std::string cell_name = cell_info->name.str(ctx);
    set_tcl_obj_string(objPtr, cell_name);
}

static void obj_dup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr)
{
    dupPtr->internalRep.twoPtrValue = srcPtr->internalRep.twoPtrValue;
}

static void obj_free(Tcl_Obj *objPtr) {}

static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s)
{
    char *copy = Tcl_Alloc(s.size() + 1);
    std::copy(s.begin(), s.end(), copy);
    copy[s.size()] = '\0';
    Tcl_SetResult(interp, copy, TCL_DYNAMIC);
}

static Tcl_ObjType port_object = {
        "port", obj_free, obj_dup, port_update_string, obj_set_from_any,
};

static Tcl_ObjType cell_object = {
        "cell", obj_free, obj_dup, cell_update_string, obj_set_from_any,
};

static int get_ports(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
    const Context *ctx = static_cast<const Context *>(data);
    if (objc == 1) {
        // Return list of all ports.
        Tcl_SetStringResult(interp, "Unimplemented");
        return TCL_ERROR;
    } else if (objc == 2) {
        const char *arg0 = Tcl_GetString(objv[1]);
        IdString port_name = ctx->id(arg0);

        auto iter = ctx->ports.find(port_name);
        if (iter == ctx->ports.end()) {
            Tcl_SetStringResult(interp, "Could not find port " + port_name.str(ctx));
            return TCL_ERROR;
        }

        Tcl_Obj *result = Tcl_NewObj();
        result->typePtr = &port_object;
        result->internalRep.twoPtrValue.ptr1 = (void *)(ctx);
        result->internalRep.twoPtrValue.ptr2 = (void *)(&iter->second);

        result->bytes = nullptr;
        port_update_string(result);

        Tcl_SetObjResult(interp, result);
        return TCL_OK;
    } else if (objc > 2) {
        log_warning("get_ports options not implemented!\n");
        return TCL_OK;
    } else {
        return TCL_ERROR;
    }
}

static int get_cells(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
    const Context *ctx = static_cast<const Context *>(data);
    if (objc == 1) {
        // Return list of all ports.
        Tcl_SetStringResult(interp, "Unimplemented");
        return TCL_ERROR;
    } else if (objc == 2) {
        const char *arg0 = Tcl_GetString(objv[1]);
        IdString cell_name = ctx->id(arg0);

        auto iter = ctx->cells.find(cell_name);
        if (iter == ctx->cells.end()) {
            Tcl_SetStringResult(interp, "Could not find cell " + cell_name.str(ctx));
            return TCL_ERROR;
        }

        Tcl_Obj *result = Tcl_NewObj();
        result->typePtr = &cell_object;
        result->internalRep.twoPtrValue.ptr1 = (void *)(ctx);
        result->internalRep.twoPtrValue.ptr2 = (void *)(iter->second.get());

        result->bytes = nullptr;
        cell_update_string(result);

        Tcl_SetObjResult(interp, result);
        return TCL_OK;
    } else if (objc > 2) {
        log_warning("get_cells options not implemented!\n");
        return TCL_OK;
    } else {
        return TCL_ERROR;
    }
}

static int set_property(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
    // set_property <property> <value> <object>
    if (objc != 4) {
        Tcl_SetStringResult(interp, "Only simple 'set_property <property> <value> <object>' is supported");
        return TCL_ERROR;
    }

    const char *property = Tcl_GetString(objv[1]);
    const char *value = Tcl_GetString(objv[2]);
    const Tcl_Obj *object = objv[3];

    if (object->typePtr == &port_object) {
        const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1);
        PortInfo *port_info = static_cast<PortInfo *>(object->internalRep.twoPtrValue.ptr2);
        NPNR_ASSERT(port_info->net != nullptr);
        CellInfo *cell = ctx->port_cells.at(port_info->name);

        cell->attrs[ctx->id(property)] = Property(value);
    } else if (object->typePtr == &cell_object) {
        const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1);
        CellInfo *cell = static_cast<CellInfo *>(object->internalRep.twoPtrValue.ptr2);

        cell->attrs[ctx->id(property)] = Property(value);
    }

    return TCL_OK;
}

static int not_implemented(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
    // TCL command that is not yet implemented
    log_warning("%s command is not implemented!\n", Tcl_GetString(objv[0]));
    return TCL_OK;
}

TclInterp::TclInterp(Context *ctx)
{
    interp = Tcl_CreateInterp();
    NPNR_ASSERT(Tcl_Init(interp) == TCL_OK);

    Tcl_RegisterObjType(&port_object);
    Tcl_RegisterObjType(&cell_object);

    NPNR_ASSERT(Tcl_Eval(interp, "rename unknown _original_unknown") == TCL_OK);
    NPNR_ASSERT(Tcl_Eval(interp, "proc unknown args {\n"
                                 "  set result [scan [lindex $args 0] \"%d\" value]\n"
                                 "  if { $result == 1 && [llength $args] == 1 } {\n"
                                 "    return \\[$value\\]\n"
                                 "  } else {\n"
                                 "    uplevel 1 [list _original_unknown {*}$args]\n"
                                 "  }\n"
                                 "}") == TCL_OK);
    Tcl_CreateObjCommand(interp, "get_ports", get_ports, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "get_cells", get_cells, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "set_property", set_property, ctx, nullptr);

    // Not implemented TCL commands
    Tcl_CreateObjCommand(interp, "create_clock", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "get_iobanks", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "get_nets", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "get_pins", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "set_false_path", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "set_max_delay", not_implemented, ctx, nullptr);
}

TclInterp::~TclInterp() { Tcl_DeleteInterp(interp); }

NEXTPNR_NAMESPACE_END
<commit_msg>interchange: xdc: add more not_implemented commands<commit_after>/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2019  gatecat <gatecat@ds0.me>
 *  Copyright (C) 2021  Symbiflow Authors
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "xdc.h"

#include <string>

#include "context.h"
#include "log.h"

// Include tcl.h late because it messed with #define's and lets them leave the
// scope of the header.
#include <tcl.h>

NEXTPNR_NAMESPACE_BEGIN

static int obj_set_from_any(Tcl_Interp *interp, Tcl_Obj *objPtr) { return TCL_ERROR; }

static void set_tcl_obj_string(Tcl_Obj *objPtr, const std::string &s)
{
    NPNR_ASSERT(objPtr->bytes == nullptr);
    // Need to have space for the end null byte.
    objPtr->bytes = Tcl_Alloc(s.size() + 1);

    // Length is length of string, not including the end null byte.
    objPtr->length = s.size();

    std::copy(s.begin(), s.end(), objPtr->bytes);
    objPtr->bytes[objPtr->length] = '\0';
}

static void port_update_string(Tcl_Obj *objPtr)
{
    const Context *ctx = static_cast<const Context *>(objPtr->internalRep.twoPtrValue.ptr1);
    PortInfo *port_info = static_cast<PortInfo *>(objPtr->internalRep.twoPtrValue.ptr2);

    std::string port_name = port_info->name.str(ctx);
    set_tcl_obj_string(objPtr, port_name);
}

static void cell_update_string(Tcl_Obj *objPtr)
{
    const Context *ctx = static_cast<const Context *>(objPtr->internalRep.twoPtrValue.ptr1);
    CellInfo *cell_info = static_cast<CellInfo *>(objPtr->internalRep.twoPtrValue.ptr2);

    std::string cell_name = cell_info->name.str(ctx);
    set_tcl_obj_string(objPtr, cell_name);
}

static void obj_dup(Tcl_Obj *srcPtr, Tcl_Obj *dupPtr)
{
    dupPtr->internalRep.twoPtrValue = srcPtr->internalRep.twoPtrValue;
}

static void obj_free(Tcl_Obj *objPtr) {}

static void Tcl_SetStringResult(Tcl_Interp *interp, const std::string &s)
{
    char *copy = Tcl_Alloc(s.size() + 1);
    std::copy(s.begin(), s.end(), copy);
    copy[s.size()] = '\0';
    Tcl_SetResult(interp, copy, TCL_DYNAMIC);
}

static Tcl_ObjType port_object = {
        "port", obj_free, obj_dup, port_update_string, obj_set_from_any,
};

static Tcl_ObjType cell_object = {
        "cell", obj_free, obj_dup, cell_update_string, obj_set_from_any,
};

static int get_ports(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
    const Context *ctx = static_cast<const Context *>(data);
    if (objc == 1) {
        // Return list of all ports.
        Tcl_SetStringResult(interp, "Unimplemented");
        return TCL_ERROR;
    } else if (objc == 2) {
        const char *arg0 = Tcl_GetString(objv[1]);
        IdString port_name = ctx->id(arg0);

        auto iter = ctx->ports.find(port_name);
        if (iter == ctx->ports.end()) {
            Tcl_SetStringResult(interp, "Could not find port " + port_name.str(ctx));
            return TCL_ERROR;
        }

        Tcl_Obj *result = Tcl_NewObj();
        result->typePtr = &port_object;
        result->internalRep.twoPtrValue.ptr1 = (void *)(ctx);
        result->internalRep.twoPtrValue.ptr2 = (void *)(&iter->second);

        result->bytes = nullptr;
        port_update_string(result);

        Tcl_SetObjResult(interp, result);
        return TCL_OK;
    } else if (objc > 2) {
        log_warning("get_ports options not implemented!\n");
        return TCL_OK;
    } else {
        return TCL_ERROR;
    }
}

static int get_cells(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
    const Context *ctx = static_cast<const Context *>(data);
    if (objc == 1) {
        // Return list of all ports.
        Tcl_SetStringResult(interp, "Unimplemented");
        return TCL_ERROR;
    } else if (objc == 2) {
        const char *arg0 = Tcl_GetString(objv[1]);
        IdString cell_name = ctx->id(arg0);

        auto iter = ctx->cells.find(cell_name);
        if (iter == ctx->cells.end()) {
            Tcl_SetStringResult(interp, "Could not find cell " + cell_name.str(ctx));
            return TCL_ERROR;
        }

        Tcl_Obj *result = Tcl_NewObj();
        result->typePtr = &cell_object;
        result->internalRep.twoPtrValue.ptr1 = (void *)(ctx);
        result->internalRep.twoPtrValue.ptr2 = (void *)(iter->second.get());

        result->bytes = nullptr;
        cell_update_string(result);

        Tcl_SetObjResult(interp, result);
        return TCL_OK;
    } else if (objc > 2) {
        log_warning("get_cells options not implemented!\n");
        return TCL_OK;
    } else {
        return TCL_ERROR;
    }
}

static int set_property(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
    // set_property <property> <value> <object>
    if (objc != 4) {
        Tcl_SetStringResult(interp, "Only simple 'set_property <property> <value> <object>' is supported");
        return TCL_ERROR;
    }

    const char *property = Tcl_GetString(objv[1]);
    const char *value = Tcl_GetString(objv[2]);
    const Tcl_Obj *object = objv[3];

    if (object->typePtr == &port_object) {
        const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1);
        PortInfo *port_info = static_cast<PortInfo *>(object->internalRep.twoPtrValue.ptr2);
        NPNR_ASSERT(port_info->net != nullptr);
        CellInfo *cell = ctx->port_cells.at(port_info->name);

        cell->attrs[ctx->id(property)] = Property(value);
    } else if (object->typePtr == &cell_object) {
        const Context *ctx = static_cast<const Context *>(object->internalRep.twoPtrValue.ptr1);
        CellInfo *cell = static_cast<CellInfo *>(object->internalRep.twoPtrValue.ptr2);

        cell->attrs[ctx->id(property)] = Property(value);
    }

    return TCL_OK;
}

static int not_implemented(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
    // TCL command that is not yet implemented
    log_warning("%s command is not implemented!\n", Tcl_GetString(objv[0]));
    return TCL_OK;
}

TclInterp::TclInterp(Context *ctx)
{
    interp = Tcl_CreateInterp();
    NPNR_ASSERT(Tcl_Init(interp) == TCL_OK);

    Tcl_RegisterObjType(&port_object);
    Tcl_RegisterObjType(&cell_object);

    NPNR_ASSERT(Tcl_Eval(interp, "rename unknown _original_unknown") == TCL_OK);
    NPNR_ASSERT(Tcl_Eval(interp, "proc unknown args {\n"
                                 "  set result [scan [lindex $args 0] \"%d\" value]\n"
                                 "  if { $result == 1 && [llength $args] == 1 } {\n"
                                 "    return \\[$value\\]\n"
                                 "  } else {\n"
                                 "    uplevel 1 [list _original_unknown {*}$args]\n"
                                 "  }\n"
                                 "}") == TCL_OK);
    Tcl_CreateObjCommand(interp, "get_ports", get_ports, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "get_cells", get_cells, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "set_property", set_property, ctx, nullptr);

    // Not implemented TCL commands
    Tcl_CreateObjCommand(interp, "create_clock", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "get_clocks", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "get_iobanks", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "get_nets", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "get_pins", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "set_clock_groups", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "set_false_path", not_implemented, ctx, nullptr);
    Tcl_CreateObjCommand(interp, "set_max_delay", not_implemented, ctx, nullptr);
}

TclInterp::~TclInterp() { Tcl_DeleteInterp(interp); }

NEXTPNR_NAMESPACE_END
<|endoftext|>