ribi::cmap::QtNode * ribi::cmap::QtRateConceptMap::AddNode(const boost::shared_ptr<Node> node)
{
  //const boost::shared_ptr<QtRateStrategy> display_strategy(new QtRateStrategy(node->GetConcept()));
  //assert(display_strategy);
  //QtNode * const qtnode = new QtNode(node,display_strategy);
  QtNode * const qtnode {
    IsCenterNode(node)
    ? new QtCenterNode(boost::dynamic_pointer_cast<CenterNode>(node))
    : new QtNode(node,GetDisplayStrategy(node->GetConcept()))
  };
  assert(qtnode);
  assert(IsCenterNode(qtnode->GetNode()) == IsQtCenterNode(qtnode)
    && "Should be equivalent");

  //General: inform an Observer that this item has changed
  qtnode->m_signal_item_has_updated.connect(
   boost::bind(&QtConceptMap::OnItemRequestsUpdate,this,boost::lambda::_1));

  //General: inform an Observer that a QGraphicsScene needs to be updated
  qtnode->m_signal_request_scene_update.connect(
    boost::bind(&QtConceptMap::OnRequestSceneUpdate,this));

  //Specific: inform an Observer that the Node requests its Concept being rated
  qtnode->m_signal_node_requests_rate_concept.connect(
    boost::bind(
      &ribi::cmap::QtRateConceptMap::OnNodeRequestsRateConcept,
      this, boost::lambda::_1)); //Do not forget the placeholder!

  //Specific: inform an Observer that the Node requests its Examples being rated
  qtnode->m_signal_node_requests_rate_examples.connect(
    boost::bind(
      &ribi::cmap::QtRateConceptMap::OnNodeRequestsRateExamples,
      this, boost::lambda::_1)); //Do not forget the placeholder!

  assert(!qtnode->scene());
  this->scene()->addItem(qtnode);

  assert(std::count(
    GetConceptMap()->GetNodes().begin(),
    GetConceptMap()->GetNodes().end(),
    node) == 1 && "Assume Node is already in the concept map");
  //this->GetConceptMap()->AddNode(node);

  assert(qtnode->pos().x() == node->GetX());
  assert(qtnode->pos().y() == node->GetY());

  //Cannot test this: during construction not all nodes are put in
  //assert(Collect<QtNode>(this->scene()).size() == this->GetConceptMap()->GetNodes().size());

  return qtnode;
}
void ribi::cmap::QtConceptMap::DeleteNode(QtNode * const qtnode)
{
  #ifndef NDEBUG
  const int n_items_before = this->scene()->items().count();
  #endif

  //Delete the edges connected to this node
  {
    const std::vector<QtEdge *> qtedges = GetQtEdges();
    const std::size_t sz = qtedges.size();
    for (std::size_t i=0; i!=sz; ++i)
    {
      QtEdge * const qtedge = qtedges[i];
      assert(qtedge);
      if (qtedge->GetFrom() == qtnode || qtedge->GetTo() == qtnode)
      {
        DeleteEdge(qtedge);
      }
    }
  }

  //Remove from non-GUI, which removes the left-overs
  GetConceptMap()->DeleteNode(qtnode->GetNode());
  //Remove node from GUI
  this->scene()->removeItem(qtnode);

  #ifndef NDEBUG
  const int n_items_after = this->scene()->items().count();
  assert(n_items_before - n_items_after >= 1 && "At least one item is deleted: one node and x edges");
  assert(Collect<QtNode>(this->scene()).size() == this->GetConceptMap()->GetNodes().size()
    && "GUI and non-GUI concept map must match");
  #endif
}
const std::vector<const ribi::cmap::QtEdge *> ribi::cmap::QtConceptMap::GetQtEdges() const
{
  const std::vector<const QtEdge *> qtedges
    = Collect<const QtEdge>(this->scene());
  assert(qtedges.size() == GetConceptMap()->GetNodes().size()
      && "GUI and non-GUI must contain an equal amount of edges");
  return qtedges;
}
const std::vector<const ribi::cmap::QtNode *> ribi::cmap::QtConceptMap::GetQtNodes() const
{
  const std::vector<const QtNode *> qtnodes
    = Collect<const QtNode>(this->scene());
  if (qtnodes.size() != GetConceptMap()->GetNodes().size())
  {
    TRACE("Warning: GUI and non-GUI contain an unequal amount of nodes");
  }

  //assert(qtnodes.size() == GetConceptMap()->GetNodes().size()
  //    && "GUI and non-GUI must contain an equal amount of nodes");
  return qtnodes;
}
void ribi::cmap::QtConceptMap::CheckInvariants() const noexcept
{
  #ifndef NDEBUG

  //If there is one QtEdge selected, its Edge must be able to been found
  try
  {
    const auto qtedge = ExtractTheOneSelectedQtEdge(*GetScene());
    //The QtEdge its edge must be in the concept map
    //Can only compare IDs, as QtEdge::GetEdge() and its equivalent in the concept map may mismatch
    assert(
      has_custom_edge_with_my_edge(
        qtedge->GetEdge(), GetConceptMap(), [](const Edge& lhs, const Edge& rhs) { return lhs.GetId() == rhs.GetId(); }
      )
    );
    const auto edge = ExtractTheOneSelectedEdge(this->GetConceptMap(), *GetScene());
    assert(qtedge->GetEdge().GetId() == edge.GetId());
  }
  catch (...) {} //No problem

  //All QtNodes must have a scene
  {
    const auto qtnodes = GetQtNodes(scene());
    for (const auto qtnode: qtnodes)
    {
      assert(qtnode);
      assert(qtnode->scene());
    }
  }

  //All QtEdges, their QtNodes and Arrows must have a scene
  {
    const auto qtedges = GetQtEdges(scene());
    for (const auto qtedge: qtedges)
    {
      assert(qtedge);
      assert(qtedge->scene());
      assert(qtedge->GetArrow());
      assert(qtedge->GetArrow()->scene());
      assert(qtedge->GetQtNode());
      assert(qtedge->GetQtNode()->scene());
      assert(qtedge->GetFrom());
      assert(qtedge->GetFrom()->scene());
      assert(qtedge->GetTo());
      assert(qtedge->GetTo()->scene());
    }
  }

  #endif
}
void ribi::cmap::QtConceptMap::DeleteEdge(QtEdge * const qtedge)
{
  #ifndef NDEBUG
  const int n_items_before = this->scene()->items().count();
  #endif

  assert(scene()->items().contains(qtedge));
  //Remove non-GUI edges
  GetConceptMap()->DeleteEdge(qtedge->GetEdge());
  //Remove GUI edge
  this->scene()->removeItem(qtedge);
  //No left-overs when deleting an edge
  //DeleteLeftovers();

  #ifndef NDEBUG
  const int n_items_after = this->scene()->items().count();
  assert(n_items_after + 1 == n_items_before);
  //Cannot do the check below: in DeleteNode multiple edges are deleted
  //assert(Collect<QtNode>(this->scene()).size() == this->GetConceptMap()->GetNodes().size()
  //  && "GUI and non-GUI concept map must match");
  #endif
}
ribi::cmap::QtConceptMap::QtConceptMap(
  const boost::shared_ptr<ConceptMap> concept_map,
  QWidget* parent)
  : QtKeyboardFriendlyGraphicsView(parent),
    m_concept_map(concept_map),
    m_examples_item(new QtExamplesItem)
{
  assert( (concept_map || !concept_map)
    && "Also empty concept maps must be displayed");
  assert(GetConceptMap() == concept_map);
  assert( (!concept_map || concept_map->IsValid())
    && "Expect no or a valid concept map");

  //Cannot test this ABC here, its derived classes will test themselves

  this->setScene(new QGraphicsScene(this));

  assert(!m_examples_item->scene());
  scene()->addItem(m_examples_item); //Add the examples so it has a parent

  assert(Collect<QtNode>(scene()).empty());

  //Without this line, mouseMoveEvent won't be called
  this->setMouseTracking(true);


  {
    //QLinearGradient linearGradient(-500,-500,500,500);
    //linearGradient.setColorAt(0.0,QColor(214,214,214));
    //linearGradient.setColorAt(1.0,QColor(255,255,255));
    assert(this->scene());
    //this->scene()->setBackgroundBrush(linearGradient);
    this->scene()->setBackgroundBrush(QBrush(QColor(255,255,255)));
  }

}
void ribi::cmap::QtConceptMap::SetConceptMap(const ConceptMap& conceptmap)
{
  RemoveConceptMap();
  m_conceptmap = conceptmap;
  assert(GetConceptMap() == conceptmap);

  assert(this->scene());

  //This std::vector keeps the QtNodes in the same order as the nodes in the concept map
  //You cannot rely on Collect<QtConceptMapNodeConcept*>(scene), as this shuffles the order
  //std::vector<QtNode*> qtnodes;

  assert(Collect<QtNode>(scene()).empty());

  //Add the nodes to the scene, if there are any
  const auto vip = vertices(m_conceptmap);
  for(auto i=vip.first; i!=vip.second; ++i)
  {
    assert(boost::num_vertices(m_conceptmap));
    const auto pmap = get(boost::vertex_custom_type, m_conceptmap);
    const Node node = get(pmap, *i);
    const bool is_focal_node{i == vip.first};
    QtNode * const qtnode{new QtNode(node)};
    if (is_focal_node) { qtnode->setFlags(0); }
    assert(qtnode);
    assert(!qtnode->scene());
    scene()->addItem(qtnode);
    assert(qtnode->scene());
    assert(FindQtNode(node.GetId(), scene()));
  }
  //Add the Edges
  const auto eip = edges(m_conceptmap);
  for(auto i = eip.first; i != eip.second; ++i)
  {
    assert(boost::num_edges(m_conceptmap));
    const VertexDescriptor vd_from = boost::source(*i, m_conceptmap);
    const VertexDescriptor vd_to = boost::target(*i, m_conceptmap);
    assert(vd_from != vd_to);
    const auto vertex_map = get(boost::vertex_custom_type, m_conceptmap);
    const Node from = get(vertex_map, vd_from);
    const Node to = get(vertex_map, vd_to);
    assert(from.GetId() != to.GetId());
    QtNode * const qtfrom = FindQtNode(from.GetId(), scene());
    QtNode * const qtto = FindQtNode(to.GetId(), scene());
    assert(qtfrom != qtto);
    const auto edge_map = get(boost::edge_custom_type, m_conceptmap);
    const Edge edge = get(edge_map, *i);
    QtEdge * const qtedge{new QtEdge(edge,qtfrom,qtto)};
    if (qtfrom->GetNode().IsCenterNode() || qtto->GetNode().IsCenterNode())
    {
      qtedge->GetQtNode()->setVisible(false);
    }

    assert(!qtedge->scene());
    assert(!qtedge->GetQtNode()->scene());
    assert(!qtedge->GetArrow()->scene());
    scene()->addItem(qtedge);
    //scene()->addItem(qtedge->GetQtNode()); //Get these for free
    //scene()->addItem(qtedge->GetArrow()); //Get these for free
    assert(qtedge->scene());
    assert(qtedge->GetQtNode()->scene());
    assert(qtedge->GetArrow()->scene());
  }
  assert(GetConceptMap() == conceptmap);

  CheckInvariants();
}
void ribi::cmap::QtConceptMap::keyPressEvent(QKeyEvent *event)
{
  CheckInvariants();
  UpdateConceptMap();
  CheckInvariants();

  switch (event->key())
  {
    case Qt::Key_F1:
    case Qt::Key_F2:
    {
      const auto items = scene()->selectedItems();
      if (items.size() != 1) {
        break;
      }
      if (QtNode * const qtnode = dynamic_cast<QtNode*>(items.front()))
      {
        OnNodeKeyDownPressed(qtnode, event->key());
      }
    }
    break;
    #ifndef NDEBUG
    case Qt::Key_F9:
    {
      throw std::runtime_error("Exception forced by user");
    }
    #endif
    case Qt::Key_Delete:
    {
      UpdateConceptMap();
      if (GetVerbosity()) { TRACE("Pressing delete"); }
      try
      {
        DoCommand(new CommandDeleteSelected(m_conceptmap, scene(), m_tools));
      }
      catch (std::logic_error& e)
      {
        if (GetVerbosity()) { TRACE(e.what()); }
      }
    }
    return;
    case Qt::Key_Escape:
    {
      if (GetVerbosity()) { TRACE("Pressing Escape"); }
      //Only remove the 'new arrow' if present
      if (m_arrow->isVisible())
      {
        if (GetVerbosity()) { TRACE("Remove the new arrow"); }
        m_arrow->hide();
        assert(!m_arrow->isVisible());
        return;
      }
    }
    break;
    case Qt::Key_Equal:
      if (GetVerbosity()) { TRACE("Pressing Qt::Key_Equal"); }
      this->scale(1.1,1.1);
      break;
    case Qt::Key_Minus:
      if (GetVerbosity()) { TRACE("Pressing Qt::Key_Minus"); }
      this->scale(0.9,0.9);
      break;
    case Qt::Key_E:
      if (event->modifiers() & Qt::ControlModifier)
      {
        if (GetVerbosity()) { TRACE("Pressing CTRL-E"); }
        try { this->DoCommand(new CommandCreateNewEdgeBetweenTwoSelectedNodes(GetConceptMap(),m_mode,scene(),m_tools)); }
        catch (std::logic_error& ) {}
      }
      return;
    case Qt::Key_T:
      if (event->modifiers() & Qt::ControlModifier)
      {
        if (GetVerbosity()) { TRACE("Pressing CTRL-T"); }
        try
        {
          const auto cmd = new CommandToggleArrowTail(GetConceptMap(), scene());
          this->DoCommand(cmd);
        }
        catch (std::logic_error& e) {}
      }
      return;
    case Qt::Key_H:
      if (event->modifiers() & Qt::ControlModifier)
      {
        if (GetVerbosity()) { TRACE("Pressing CTRL-H"); }
        try
        {
          const auto cmd = new CommandToggleArrowHead(GetConceptMap(), scene());
          this->DoCommand(cmd);
        }
        catch (std::logic_error& e) {}
      }
      return;
    case Qt::Key_N:
      if (event->modifiers() & Qt::ControlModifier)
      {
        if (GetVerbosity()) { TRACE("Pressing CTRL-N"); }
        try { this->DoCommand(new CommandCreateNewNode(m_conceptmap,m_mode,scene(),m_tools,0.0,0.0)); }
        catch (std::logic_error& ) {}
      }
      return;
    case Qt::Key_Z:
      if (event->modifiers() & Qt::ControlModifier)
      {
        if (event->modifiers() & Qt::ShiftModifier)
        {
          this->m_undo.redo();
        }
        else
        {
          this->m_undo.undo();
        }
      }
      return;
    case Qt::Key_Question:
      if (GetVerbosity()) { TRACE("Pressing Qt::Key_Question"); }
      UpdateConceptMap();
      break;

  }

  for (auto qtedge: GetSelectedQtEdges(*GetScene())) {
    qtedge->keyPressEvent(event);
    qtedge->update();
  }
  QtKeyboardFriendlyGraphicsView::keyPressEvent(event);
  UpdateConceptMap();

  CheckInvariants();
}