text: /*
   * Copyright (c) 2012, Willow Garage, Inc.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *
   *     * Redistributions of source code must retain the above copyright
   *       notice, this list of conditions and the following disclaimer.
   *     * Redistributions in binary form must reproduce the above copyright
   *       notice, this list of conditions and the following disclaimer in the
   *       documentation and/or other materials provided with the distribution.
   *     * Neither the name of the Willow Garage, Inc. nor the names of its
   *       contributors may be used to endorse or promote products derived from
   *       this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   */
  
  #include <QStringList>
  
  #include "rviz/properties/quaternion_property.h"
  #include "rviz/properties/property_tree_model.h"
  
  namespace rviz
  {
  
  QuaternionProperty::QuaternionProperty( const QString& name,
                                          const Ogre::Quaternion& default_value,
                                          const QString& description,
                                          Property* parent,
                                          const char *changed_slot,
                                          QObject* receiver )
    : Property( name, QVariant(), description, parent, changed_slot, receiver )
    , quaternion_( default_value )
    , ignore_child_updates_( false )
  {
    x_ = new Property( "X", quaternion_.x, "X coordinate", this );
    y_ = new Property( "Y", quaternion_.y, "Y coordinate", this );
    z_ = new Property( "Z", quaternion_.z, "Z coordinate", this );
    w_ = new Property( "W", quaternion_.w, "W coordinate", this );
    updateString();
    connect( x_, SIGNAL( aboutToChange() ), this, SLOT( emitAboutToChange() ));
    connect( y_, SIGNAL( aboutToChange() ), this, SLOT( emitAboutToChange() ));
    connect( z_, SIGNAL( aboutToChange() ), this, SLOT( emitAboutToChange() ));
    connect( w_, SIGNAL( aboutToChange() ), this, SLOT( emitAboutToChange() ));
    connect( x_, SIGNAL( changed() ), this, SLOT( updateFromChildren() ));
    connect( y_, SIGNAL( changed() ), this, SLOT( updateFromChildren() ));
    connect( z_, SIGNAL( changed() ), this, SLOT( updateFromChildren() ));
    connect( w_, SIGNAL( changed() ), this, SLOT( updateFromChildren() ));
  }
  
  bool QuaternionProperty::setQuaternion( const Ogre::Quaternion& new_quaternion )
  {
    if( new_quaternion != quaternion_ ) {
      Q_EMIT aboutToChange();
      quaternion_ = new_quaternion;
      ignore_child_updates_ = true;
      x_->setValue( quaternion_.x );
      y_->setValue( quaternion_.y );
      z_->setValue( quaternion_.z );
      w_->setValue( quaternion_.w );
      ignore_child_updates_ = false;
      updateString();
      Q_EMIT changed();
      if( model_ )
        model_->emitDataChanged( this );
      return true;
    }
    return false;
  }
  
  bool QuaternionProperty::setValue( const QVariant& new_value )
  {
    QStringList strings = new_value.toString().split( ';' );
    if( strings.size() >= 4 )
    {
      bool x_ok = true;
      float x = strings[ 0 ].toFloat( &x_ok );
      bool y_ok = true;
      float y = strings[ 1 ].toFloat( &y_ok );
      bool z_ok = true;
      float z = strings[ 2 ].toFloat( &z_ok );
      bool w_ok = true;
      float w = strings[ 3 ].toFloat( &w_ok );
      if( x_ok && y_ok && z_ok && w_ok )
      {
        return setQuaternion( Ogre::Quaternion( w, x, y, z ));
      }
    }
    return false;
  }
  
  void QuaternionProperty::updateFromChildren()
  {
    if( !ignore_child_updates_ )
    {
      quaternion_.x = x_->getValue().toFloat();
      quaternion_.y = y_->getValue().toFloat();
      quaternion_.z = z_->getValue().toFloat();
      quaternion_.w = w_->getValue().toFloat();
      updateString();
      Q_EMIT changed();
    }
  }
  
  void QuaternionProperty::emitAboutToChange()
  {
    if( !ignore_child_updates_ )
    {
      Q_EMIT aboutToChange();
    }
  }
  
  void QuaternionProperty::updateString()
  {
    value_ = QString( "%1; %2; %3; %4" )
      .arg( quaternion_.x, 0, 'g', 5 )
      .arg( quaternion_.y, 0, 'g', 5 )
      .arg( quaternion_.z, 0, 'g', 5 )
      .arg( quaternion_.w, 0, 'g', 5 );
  }
  
  void QuaternionProperty::load( const Config& config )
  {
    float x, y, z, w;
    if( config.mapGetFloat( "X", &x ) &&
        config.mapGetFloat( "Y", &y ) &&
        config.mapGetFloat( "Z", &z ) &&
        config.mapGetFloat( "W", &w ))
    {
      // Calling setQuaternion() once explicitly is better than letting
      // the Property class load the X, Y, Z, and W children
      // independently, which would result in at least 4 calls to
      // setQuaternion().
      setQuaternion( Ogre::Quaternion( w, x, y, z ));
    }
  }
  
  void QuaternionProperty::save( Config config ) const
  {
    // Saving the child values explicitly avoids having Property::save()
    // save the summary string version of the property.
    config.mapSetValue( "X", x_->getValue() );
    config.mapSetValue( "Y", y_->getValue() );
    config.mapSetValue( "Z", z_->getValue() );
    config.mapSetValue( "W", w_->getValue() );
  }
  
  void QuaternionProperty::setReadOnly( bool read_only )
  {
    Property::setReadOnly( read_only );
    x_->setReadOnly( read_only );
    y_->setReadOnly( read_only );
    z_->setReadOnly( read_only );
    w_->setReadOnly( read_only );
  }
  
  
  } // end namespace rviz
  