text: #include <pcl/apps/cloud_composer/qt.h>
  #include <pcl/apps/cloud_composer/toolbox_model.h>
  #include <pcl/apps/cloud_composer/tool_interface/abstract_tool.h>
  #include <pcl/apps/cloud_composer/tool_interface/tool_factory.h>
  #include <pcl/apps/cloud_composer/project_model.h>
  #include <pcl/apps/cloud_composer/items/cloud_composer_item.h>
  
  pcl::cloud_composer::ToolBoxModel::ToolBoxModel (QTreeView* tool_view, QTreeView* parameter_view_, QObject* parent)
  : QStandardItemModel (parent)
  , tool_view_ (tool_view)
  , parameter_view_ (parameter_view_)
  , project_model_ (0)
  {
    
  }
  
  pcl::cloud_composer::ToolBoxModel::ToolBoxModel (const ToolBoxModel&)
    : QStandardItemModel ()
  {
  }
  
  pcl::cloud_composer::ToolBoxModel::~ToolBoxModel ()
  {
  }
  
  void
  pcl::cloud_composer::ToolBoxModel::addTool (ToolFactory* tool_factory)
  {
    //qDebug () << "Icon name:"<< tool_factory->getIconName ();
    QIcon new_tool_icon = QIcon (tool_factory->getIconName ());
    QStandardItem* new_tool_item = new QStandardItem (new_tool_icon, tool_factory->getPluginName ());
    new_tool_item->setEditable (false);
    
    new_tool_item->setData (QVariant::fromValue (tool_factory), FACTORY);
    PropertiesModel* new_tool_parameters= tool_factory->createToolParameterModel (this);  
    new_tool_item->setData (QVariant::fromValue (new_tool_parameters), PARAMETER_MODEL);
    
    tool_items.insert (new_tool_item);
    QStandardItem* group_item = addToolGroup (tool_factory->getToolGroupName ());
    group_item->appendRow (new_tool_item); 
    //Expand the view for this tool group
    QModelIndex group_index = this->indexFromItem(group_item);
    tool_view_->setExpanded (group_index, true);
    
  }
  
  void
  pcl::cloud_composer::ToolBoxModel::setSelectionModel (QItemSelectionModel* selection_model)
  {
    selection_model_ = selection_model;
  }
    
  QStandardItem*
  pcl::cloud_composer::ToolBoxModel::addToolGroup (QString tool_group_name)
  {
    QList <QStandardItem*> matches_name = findItems (tool_group_name);
    if (matches_name.size () == 0)
    {
      QStandardItem* new_group_item = new QStandardItem (tool_group_name);
      appendRow (new_group_item);
      new_group_item->setSelectable (false);
      new_group_item->setEditable (false);
  
      return new_group_item;
    }
    else if (matches_name.size () > 1)
    {
      qWarning () << "Multiple tool groups with same name in ToolBoxModel!!";
    }
    
    return matches_name.value (0);
    
  }
  
  void 
  pcl::cloud_composer::ToolBoxModel::activeProjectChanged(ProjectModel* new_model, ProjectModel*)
  {
    //Disconnect old project model signal for selection change
    if (project_model_)
    {
      disconnect (project_model_->getSelectionModel (), SIGNAL (selectionChanged (QItemSelection,QItemSelection)),
                  this, SLOT (selectedItemChanged (QItemSelection,QItemSelection)));
      disconnect (project_model_, SIGNAL (modelChanged()),
                  this, SLOT (modelChanged()));
      
    } 
    qDebug () << "Active project changed in ToolBox Model!";
    project_model_ = new_model;
    
    //Update enabled tools, make connection for doing this automatically
    if (project_model_)
    {  
      updateEnabledTools (project_model_->getSelectionModel ()->selection ());
      connect (project_model_->getSelectionModel (), SIGNAL (selectionChanged (QItemSelection,QItemSelection)),
                  this, SLOT (selectedItemChanged (QItemSelection,QItemSelection)));
      connect (project_model_, SIGNAL (modelChanged()),
               this, SLOT (modelChanged()));
    }
  
  }
  
  void
  pcl::cloud_composer::ToolBoxModel::selectedToolChanged (const QModelIndex & current, const QModelIndex &)
  {
    //qDebug() << "Selected Tool changed";
    if (!parameter_view_)
    {
      qCritical () << "Toolbox parameter view not set!!!";
      return;
    }  
    QVariant parameter_model = current.data (PARAMETER_MODEL);
    parameter_view_->setModel ( parameter_model.value <PropertiesModel*> ());
    parameter_view_->expandAll ();
  }
  
  
  void
  pcl::cloud_composer::ToolBoxModel::toolAction ()
  {
    QModelIndex current_index = selection_model_->currentIndex ();
    if (!current_index.isValid ())
    {
      QMessageBox::warning (qobject_cast<QWidget *>(this->parent ()), "No Tool Selected", "Cannot execute action, no tool selected!");
      return;
    }
    ToolFactory* tool_factory = (current_index.data (FACTORY)).value <ToolFactory*> ();
    PropertiesModel* parameter_model = (current_index.data (PARAMETER_MODEL)).value <PropertiesModel*> ();
    //
    AbstractTool* tool = tool_factory->createTool (parameter_model);
    
    emit enqueueToolAction (tool);
  }
  
  void 
  pcl::cloud_composer::ToolBoxModel::selectedItemChanged ( const QItemSelection & selected, const QItemSelection &)
  {
    updateEnabledTools (selected);
  }
  
  void 
  pcl::cloud_composer::ToolBoxModel::enableAllTools ()
  {
    foreach (QStandardItem* tool, tool_items)
    {
      tool->setEnabled (true);
    }
  }
  
  void
  pcl::cloud_composer::ToolBoxModel::modelChanged ()
  {
    updateEnabledTools (project_model_->getSelectionModel ()->selection ());  
  }
  void
  pcl::cloud_composer::ToolBoxModel::updateEnabledTools (const QItemSelection current_selection)
  {
    //qDebug () << "UPDATING ENABLED TOOLS!";
    QModelIndexList current_indices = current_selection.indexes ();
    QMultiMap < int, QStandardItem* > type_items_map;
    foreach (QModelIndex current, current_indices)
    {
      if (current.isValid ())
      {
        QStandardItem* current_item = project_model_->itemFromIndex (current);
        type_items_map.insert (current_item->type (), current_item);
      }
    }
    enableAllTools ();
    QList <QStandardItem*> enabled_tools = tool_items.toList (); 
    QMap <QStandardItem*,QString> disabled_tools;
    QMutableListIterator<QStandardItem*> enabled_itr(enabled_tools);
    //Go through tools, removing from enabled list if they fail to pass tests
    while (enabled_itr.hasNext()) 
    {
      QStandardItem* tool_item = enabled_itr.next ();
      ToolFactory* tool_factory = (tool_item->data (FACTORY)).value <ToolFactory*> ();
      CloudComposerItem::ItemType input_type = tool_factory->getInputItemType ();
      QList <CloudComposerItem::ItemType> required_children_types = tool_factory->getRequiredInputChildrenTypes();
      //Check if enough items for tool are selected
      if ( tool_factory-> getNumInputItems() > current_indices.size() )
      {
          enabled_itr.remove ();
          disabled_tools.insert (tool_item, tr("Tool Requires %1 Items (%2 Selected)").arg(tool_factory-> getNumInputItems()).arg(current_indices.size ()));
      }
      //Check if selection includes at least one item with correct input type
      else if ( ! type_items_map.keys ().contains (input_type))
      {
        enabled_itr.remove ();
        disabled_tools.insert (tool_item, tr("Tool Requires item type %1 selected").arg (ITEM_TYPES_STRINGS.value (input_type - QStandardItem::UserType)));
      }
      //Check if any of selected items have required children
      else if ( required_children_types.size () > 0)
      {  
        QList <QStandardItem*> matching_selected_items = type_items_map.values (input_type);
        bool found_valid_items = false;
        QList <CloudComposerItem::ItemType> missing_children = required_children_types;
        foreach (QStandardItem* item, matching_selected_items)
        {
          QList <CloudComposerItem::ItemType> found_children_types;
          if (!item->hasChildren ())
            continue;
          
          //Find types of all children
          for (int i = 0; i < item->rowCount(); ++i)
            found_children_types.append ( static_cast<CloudComposerItem::ItemType>(item->child (i)->type ()));
          //Make temporary copy, remove type from it if is present as child
          QList <CloudComposerItem::ItemType> req_children_temp = required_children_types;
          foreach (CloudComposerItem::ItemType type, found_children_types)
            req_children_temp.removeAll (type);
          //If temporary is empty, we found all required children
          if (req_children_temp.isEmpty ())
          {
            found_valid_items = true;
            break;
          }
          //Otherwise, set missing children list
          if (req_children_temp.size () < missing_children.size ())
            missing_children = req_children_temp;
  
  
        }
        //If we didn't find all required children
        if (!found_valid_items)
        {
          enabled_itr.remove ();
          QString missing_children_string;
          foreach (CloudComposerItem::ItemType type, missing_children)
            missing_children_string.append (" "+ITEM_TYPES_STRINGS.value (type - QStandardItem::UserType));
          disabled_tools.insert (tool_item, tr ("Tool Requires child item of type(s) %1").arg (missing_children_string));
        }
      }
    }
    foreach (QStandardItem* tool, tool_items)
    {
      if (enabled_tools.contains (tool))
      {
        //qDebug () << tool->text() << " is enabled!";
        tool->setToolTip (tool->text() + " is enabled");
      }
      else
      {
       // qDebug () << tool->text() << " disabled: "<<disabled_tools.value (tool);
        tool->setToolTip (disabled_tools.value (tool));
        tool->setEnabled (false);
      }
    }
    
    
    
  }
  