Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) QtConceptMap

 

Technical facts

 

 

 

 

 

 

./CppQtConceptMap/CppQtConceptMap.pri

 

INCLUDEPATH += \
    ../../Classes/CppQtConceptMap


OTHER_FILES += \
    ../../Classes/CppQtConceptMap/Licence.txt

SOURCES += \
    ../../Classes/CppQtConceptMap/qtconceptmapbrushfactory.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapcompetency.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapconcepteditdialog.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmap.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapexamplesitem.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapitemhighlighter.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapnewarrow.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmaptoolsitem.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmaprateconceptdialognewname.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmaprateconcepttallydialognewname.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmaprateexamplesdialognewname.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapelement.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapratedconceptdialog.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapwidget.cpp \
    ../../Classes/CppQtConceptMap/qteditconceptmap.cpp \
    ../../Classes/CppQtConceptMap/qtdisplayconceptmap.cpp \
    ../../Classes/CppQtConceptMap/qtrateconceptmap.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapcenternode.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapexampledialog.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapexamplesdialog.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapconceptdialog.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapnodedialog.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapedgedialog.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapqtnodedialog.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedgedialog.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapqtnodefactory.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedgefactory.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapqtnode.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapbrating.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedge.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedge1.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedge2.cpp \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedge_test.cpp \
    ../../Classes/CppQtConceptMap/test_qtconceptmapwidget.cpp \
    ../../Classes/CppQtConceptMap/test_qtconceptmap.cpp \
    ../../Classes/CppQtConceptMap/test_qteditconceptmap.cpp \
    ../../Classes/CppQtConceptMap/test_qtrateconceptmap.cpp

HEADERS += \
    ../../Classes/CppQtConceptMap/qtconceptmapexamplesitem.h \
    ../../Classes/CppQtConceptMap/qtconceptmapfwd.h \
    ../../Classes/CppQtConceptMap/qtconceptmapitemhighlighter.h \
    ../../Classes/CppQtConceptMap/qtconceptmapnewarrow.h \
    ../../Classes/CppQtConceptMap/qtconceptmaptoolsitem.h \
    ../../Classes/CppQtConceptMap/qtconceptmapconcepteditdialog.h \
    ../../Classes/CppQtConceptMap/qtconceptmapbrushfactory.h \
    ../../Classes/CppQtConceptMap/qtconceptmapcompetency.h \
    ../../Classes/CppQtConceptMap/qtconceptmap.h \
    ../../Classes/CppQtConceptMap/qtconceptmaprateconceptdialognewname.h \
    ../../Classes/CppQtConceptMap/qtconceptmaprateconcepttallydialognewname.h \
    ../../Classes/CppQtConceptMap/qtconceptmaprateexamplesdialognewname.h \
    ../../Classes/CppQtConceptMap/qtconceptmapelement.h \
    ../../Classes/CppQtConceptMap/qtconceptmapratedconceptdialog.h \
    ../../Classes/CppQtConceptMap/qtconceptmapwidget.h \
    ../../Classes/CppQtConceptMap/qteditconceptmap.h \
    ../../Classes/CppQtConceptMap/qtdisplayconceptmap.h \
    ../../Classes/CppQtConceptMap/qtrateconceptmap.h \
#   ../../Classes/CppQtConceptMap/qtitemdisplaystrategy.h \
#    ../../Classes/CppQtConceptMap/qtconceptmapcenternode.h \
    ../../Classes/CppQtConceptMap/qtconceptmapexampledialog.h \
    ../../Classes/CppQtConceptMap/qtconceptmapexamplesdialog.h \
    ../../Classes/CppQtConceptMap/qtconceptmapconceptdialog.h \
    ../../Classes/CppQtConceptMap/qtconceptmapnodedialog.h \
    ../../Classes/CppQtConceptMap/qtconceptmapedgedialog.h \
    ../../Classes/CppQtConceptMap/qtconceptmapqtnodedialog.h \
    ../../Classes/CppQtConceptMap/qtconceptmapedgefactory.h \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedgedialog.h \
    ../../Classes/CppQtConceptMap/qtconceptmapqtnodefactory.h \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedgefactory.h \
    ../../Classes/CppQtConceptMap/qtconceptmapqtnode.h \
    ../../Classes/CppQtConceptMap/qtconceptmaprating.h \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedge.h \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedge1.h \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedge2.h \
    ../../Classes/CppQtConceptMap/qtconceptmapcollect.h

FORMS += \
    ../../Classes/CppQtConceptMap/qtconceptmapconcepteditdialog.ui \
    ../../Classes/CppQtConceptMap/qtconceptmaprateconceptdialognewname.ui \
    ../../Classes/CppQtConceptMap/qtconceptmaprateconcepttallydialognewname.ui \
    ../../Classes/CppQtConceptMap/qtconceptmaprateexamplesdialognewname.ui \
    ../../Classes/CppQtConceptMap/qtconceptmapratedconceptdialog.ui \
    ../../Classes/CppQtConceptMap/qtconceptmapexampledialog.ui \
    ../../Classes/CppQtConceptMap/qtconceptmapexamplesdialog.ui \
    ../../Classes/CppQtConceptMap/qtconceptmapconceptdialog.ui \
    ../../Classes/CppQtConceptMap/qtconceptmapnodedialog.ui \
    ../../Classes/CppQtConceptMap/qtconceptmapqtnodedialog.ui \
    ../../Classes/CppQtConceptMap/qtconceptmapedgedialog.ui \
    ../../Classes/CppQtConceptMap/qtconceptmapqtedgedialog.ui

RESOURCES += \
    ../../Classes/CppQtConceptMap/CppQtConceptMap.qrc

 

 

 

 

 

./CppQtConceptMap/qtconceptmap.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPCONCEPTMAP_H
#define QTCONCEPTMAPCONCEPTMAP_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtkeyboardfriendlygraphicsview.h"
#include "qtconceptmapfwd.h"
#include "qtconceptmapqtedge.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

///QtConceptMap displays a ConceptMap
///It does not offer UI interaction with the user
///QtConceptMap does offer UI interaction
class QtConceptMap : public ribi::QtKeyboardFriendlyGraphicsView
{
  Q_OBJECT

public:
  explicit QtConceptMap(QWidget* parent = 0);
  QtConceptMap(const QtConceptMap&) = delete;
  QtConceptMap& operator=(const QtConceptMap&) = delete;
  virtual ~QtConceptMap() noexcept;

  #ifndef NDEBUG
  ///Creates a new derived class
  ///A simpler alternative to Clone (see above)
  virtual std::unique_ptr<QtConceptMap> CreateNewDerived() const = 0;

  ///Do something random, used in debugging
  virtual void DoRandomStuff() = 0;
  #endif

  ///Obtain the concept map
  boost::shared_ptr<const ConceptMap> GetConceptMap() const noexcept { return m_concept_map; }
  boost::shared_ptr<      ConceptMap> GetConceptMap()       noexcept { return m_concept_map; }

  ///Obtain the read-only Qt edge items
  ///Read-and-write Qt edge items are only supported for QtEditConceptMap
  std::vector<const QtEdge *> GetQtEdges() const;

  ///Obtain the read-only Qt node items
  ///Read-and-write Qt node items are only supported for QtEditConceptMap
  std::vector<const QtNode *> GetQtNodes() const;

  ///Obtain the QGraphicsScene
  QGraphicsScene* GetScene() const noexcept;

  static std::string GetVersion() noexcept;
  static std::vector<std::string> GetVersionHistory() noexcept;


  void RemoveExamplesItem() noexcept { SetExamplesItem(nullptr); }

  void SetConceptMap(const boost::shared_ptr<ConceptMap> concept_map);

  #ifndef NDEBUG
  ///Shuffle the concepts (used in debugging)
  void Shuffle() noexcept;

  ///Test this class with a derived class instance
  static void Test(const boost::shared_ptr<const QtConceptMap>& concept_map) noexcept;
  #endif

public slots:

  virtual void keyPressEvent(QKeyEvent *event) noexcept;

protected:

  ///Adds an Edge and connects (some of) its signals to slots present in the derived classes
  ///Edge cannot be const, as an Edge has a Concept that the user might want to edit
  virtual QtEdge * AddEdge(const boost::shared_ptr<Edge> edge) = 0;

  ///Adds a node and connects (some of) its signals to slots present in the derived classes
  ///It returns (the derived class of) the QtConceptMapNodeConcept added to the scene
  virtual QtNode * AddNode(const boost::shared_ptr<Node> node) = 0;


  ///Remove all Qt and non-Qt items and add new ones
  virtual void CleanMe() = 0;

  ///Delete a QtEdge
  void DeleteEdge(QtEdge * const edge);

  ///Delete a Node
  //void DeleteNode(const boost::shared_ptr<QtNode>& node);
  void DeleteNode(const QtNode * const node);

  ///Get all the edges connected to the concept
  std::vector<QtEdge*> FindEdges(const QtNode * const from) const noexcept;

  //Find the Qt edge with the same from and to
  const QtEdge * FindQtEdgeConst(const boost::shared_ptr<const Edge> edge) const noexcept;
        QtEdge * FindQtEdge(const boost::shared_ptr<      Edge> edge)       noexcept;
  const QtEdge * FindQtEdge(     const QtEdge* const edge) const noexcept { return FindQtEdgeConst(edge); }
  const QtEdge * FindQtEdgeConst(const QtEdge* const edge) const noexcept;
        QtEdge * FindQtEdge(const QtEdge* const edge)       noexcept;
  const QtEdge * FindQtEdgeConst(
    const QtNode* const from,
    const QtNode* const to) const noexcept;

  ///Find the QtNode containing the Node
  //QtNode * FindQtNode(boost::shared_ptr<Node> node) const { return FindQtNode(node.get()); }
  const QtNode * FindQtNodeConst(const Node * const node) const noexcept;
  //const QtNode * FindQtNode     (const Node * const node) const noexcept { return FindQtNodeConst(node); }
        QtNode * FindQtNode(           Node * const node)       noexcept;

  ///Obtain the center node
  const QtNode * GetCenterNode() const noexcept;
        QtNode * GetCenterNode()       noexcept;

  ///Obtain the read-and-write Qt edge items
  ///The read-only Qt edge items is already supplied by QtConceptMap
  std::vector<QtEdge *> GetQtEdges();

  ///Obtain the rectangle with text showing the examples
  const QtExamplesItem * GetExamplesItem() const;

  ///Obtain the rectangle with text showing the examples
  QtExamplesItem * GetExamplesItem();

  ///Obtain the first QtNode under the cursor
  ///Returns nullptr if none is present
  QtNode* GetItemBelowCursor(const QPointF& pos) const;

  ///Check if this item is the center node
  static bool IsQtCenterNode(const QGraphicsItem* const item);

  ///Have the nodes in the concept map be positioned once already, or must
  ///these be (re)positioned. '(re)', because the nodes are initially positioned at the origin
  bool MustReposition(const std::vector<boost::shared_ptr<const cmap::Node> >& nodes) const;

  ///All items from a ConceptMap are put in at the center and need to be repositioned
  void RepositionItems();

  ///Set the rectangle with text showing the examples
  void SetExamplesItem(QtExamplesItem * const item);

  #ifndef NDEBUG
  ///Test the internals of this class:
  ///Does the current content really reflect the map
  void TestMe(const boost::shared_ptr<const ConceptMap> map) const;
  #endif

private:

  ///The concept map to work on, the Model
  boost::shared_ptr<ConceptMap> m_concept_map;

  ///The item showing the examples
  QtExamplesItem * m_examples_item;

  ///Implemention of OnItemUpdateRequest
  virtual void OnItemRequestUpdateImpl(const QGraphicsItem* const item) = 0;

public slots:

  ///Called whenever a concept is clicked or moved
  ///If item is nullptr, the last item might be deleted
  ///Use QGraphicsItem* due to QtKeyboardFriendlyGraphicsView working on QGraphicsItems
  ///Is implemented by OnItemUpdateRequestImpl
  void OnItemRequestsUpdate(const QGraphicsItem* const item);

  ///Called when an item requests a scene update
  void OnRequestSceneUpdate();

  friend class QtConceptMapWidget;
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPCONCEPTMAP_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmap.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmap.h"

#include <set>

#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/numeric/conversion/cast.hpp>

#include <QGraphicsScene>
#include <QKeyEvent>

#include "fuzzy_equal_to.h"
#include "qtconceptmapcollect.h"
#include "conceptmapcenternode.h"
#include "conceptmapconceptfactory.h"
#include "conceptmapconcept.h"
#include "conceptmapfactory.h"
#include "conceptmap.h"
#include "conceptmapedgefactory.h"
#include "conceptmapedge.h"
#include "conceptmaphelper.h"
#include "conceptmapnodefactory.h"
#include "conceptmapnode.h"
#include "qtarrowitem.h"
#include "qtconceptmapdisplaystrategy.h"
#include "qtconceptmapbrushfactory.h"
#include "qtconceptmapcenternode.h"
#include "qtconceptmapconcepteditdialog.h"
#include "qtconceptmapelement.h"
#include "qtconceptmapqtedge.h"
#include "qtconceptmapqtnode.h"
#include "qtconceptmapexamplesitem.h"
#include "qtconceptmapitemhighlighter.h"
#include "qtconceptmapnewarrow.h"
#include "qtconceptmapqtnode.h"
#include "qtconceptmaptoolsitem.h"
#include "qtscopeddisable.h"
#include "trace.h"
#include "xml.h"
#pragma GCC diagnostic pop


///Returns a sorted vector
template <class T>
const std::vector<T> Sort(const std::vector<T>& v)
{
  std::vector<T> w(v);
  std::sort(w.begin(),w.end());
  return w;
}

template <>
const std::vector<ribi::cmap::QtNode*>
  Sort(
    const std::vector<ribi::cmap::QtNode*>& v)
{
  typedef std::vector<ribi::cmap::QtNode*>::iterator Iterator;
  std::vector<ribi::cmap::QtNode*> w(v);
  std::sort(w.begin(),w.end());
  const Iterator i = std::find_if(w.begin(),w.end(),
    [](const ribi::cmap::QtNode* const node)
    {
      return dynamic_cast<const ribi::cmap::QtCenterNode*>(node);
    }
  );
  if (i != w.end())
  {
    std::swap(*i,*w.begin());
    assert(dynamic_cast<const ribi::cmap::QtCenterNode*>(*w.begin()));
  }
  return w;
}

ribi::cmap::QtConceptMap::QtConceptMap(QWidget* parent)
  : QtKeyboardFriendlyGraphicsView(parent),
    m_concept_map{},
    m_examples_item(new QtExamplesItem)
{
  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)));
  }
}


ribi::cmap::QtConceptMap::~QtConceptMap() noexcept
{
  //for (auto qtnode: Collect<QtNode>(scene()))
  {
    //signal #1
    //qtnode->m_signal_request_scene_update.disconnect(
    //  boost::bind(&ribi::cmap::QtConceptMap::OnRequestSceneUpdate,this)
    //);
    //signal #2
    //qtnode->m_signal_item_has_updated.disconnect(
    //  boost::bind(&ribi::cmap::QtConceptMap::OnItemRequestsUpdate,this,boost::lambda::_1)
    //);
  }

  delete m_examples_item;
  m_examples_item = nullptr;
}

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
}

void ribi::cmap::QtConceptMap::DeleteNode(const 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 node from model
  GetConceptMap()->DeleteNode(qtnode->GetNode());
  //Remove node from view
  this->scene()->removeItem(const_cast<QtNode*>(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
}

std::vector<ribi::cmap::QtEdge*> ribi::cmap::QtConceptMap::FindEdges(
  const QtNode* const from) const noexcept
{
  assert(from);
  const std::vector<QtEdge*> v = Collect<QtEdge>(scene());
  std::vector<QtEdge*> w;
  std::copy_if(v.begin(),v.end(),std::back_inserter(w),
    [from](const QtEdge* const edge)
    {
      return *edge->GetFrom() == *from || *edge->GetTo() == *from;
    }
  );
  return w;
}

const ribi::cmap::QtEdge * ribi::cmap::QtConceptMap::FindQtEdgeConst(
  const boost::shared_ptr<const Edge> edge) const noexcept
{
  const auto v(GetQtEdges());
  for (const auto e:v)
  {
    if (e->GetEdge() == edge) return e;
  }
  return nullptr;
}

ribi::cmap::QtEdge * ribi::cmap::QtConceptMap::FindQtEdge(
  const boost::shared_ptr<Edge> edge) noexcept
{
  //Calls the const version of this member function
  //To avoid duplication in const and non-const member functions [1]
  //[1] Scott Meyers. Effective C++ (3rd edition). ISBN: 0-321-33487-6.
  //    Item 3, paragraph 'Avoid duplication in const and non-const member functions'
  QtEdge * const qtedge(
    const_cast<QtEdge *>(
      dynamic_cast<const QtConceptMap*>(this)->FindQtEdgeConst(edge)
    )
  );
  return qtedge;
}

ribi::cmap::QtEdge * ribi::cmap::QtConceptMap::FindQtEdge(
  const QtEdge* const edge) noexcept
{
  //Calls the const version of this member function
  //To avoid duplication in const and non-const member functions [1]
  //[1] Scott Meyers. Effective C++ (3rd edition). ISBN: 0-321-33487-6.
  //    Item 3, paragraph 'Avoid duplication in const and non-const member functions'
  QtEdge * const qtedge(
    const_cast<QtEdge *>(
      dynamic_cast<const QtConceptMap*>(this)->FindQtEdgeConst(edge)
    )
  );
  return qtedge;
}

const ribi::cmap::QtEdge * ribi::cmap::QtConceptMap::FindQtEdgeConst(
  const QtEdge* const edge) const noexcept
{
  //return FindQtEdgeConst(edge->GetFrom().get(),edge->GetTo().get());
  return FindQtEdgeConst(edge->GetFrom(),edge->GetTo());
}

const ribi::cmap::QtEdge * ribi::cmap::QtConceptMap::FindQtEdgeConst(
  const QtNode* const from,
  const QtNode* const to) const noexcept
{

  assert(from);
  assert(to);
  assert(from != to);
  const std::vector<QtEdge*> edge_concepts = Collect<QtEdge>(scene());
  const auto iter = std::find_if(edge_concepts.begin(),edge_concepts.end(),
    [from,to](const QtEdge* const edge)
    {
      return
        (*edge->GetFrom() == *from && *edge->GetTo() == *to)
     || (*edge->GetFrom() == *to && *edge->GetTo() == *from);
    }
  );
  if (iter == edge_concepts.end()) return nullptr;
  return * iter;
}

/*
ribi::cmap::QtNode * ribi::cmap::QtConceptMap::FindQtNode(Node * const node) noexcept
{
  return const_cast<QtNode *>(FindQtNodeConst(node));
}
*/

const ribi::cmap::QtNode * ribi::cmap::QtConceptMap::FindQtNodeConst(
  const ribi::cmap::Node * const node) const noexcept
{
  assert(node);
  const std::vector<QtNode *> qtnodes = Collect<QtNode>(scene());
  for (QtNode * qtnode: qtnodes)
  {
    if (qtnode->GetNode().get() == node) return qtnode;
  }
  return nullptr;
}

ribi::cmap::QtNode * ribi::cmap::QtConceptMap::FindQtNode(
  ribi::cmap::Node * const node) noexcept
{
  assert(node);
  //Calls the const version of this member function
  //To avoid duplication in const and non-const member functions [1]
  //[1] Scott Meyers. Effective C++ (3rd edition). ISBN: 0-321-33487-6.
  //    Item 3, paragraph 'Avoid duplication in const and non-const member functions'
  QtNode * const qtnode(
    const_cast<QtNode *>(
      dynamic_cast<const QtConceptMap*>(this)->FindQtNodeConst(node)
    )
  );
  return qtnode;
}

const ribi::cmap::QtNode * ribi::cmap::QtConceptMap::GetCenterNode() const noexcept
{

  assert(scene());
  assert(!scene()->items().isEmpty());
  assert(scene()->items()[0]);
  QList<QGraphicsItem *> v = scene()->items();
  assert(std::count_if(v.begin(),v.end(),
    [this](const QGraphicsItem * const item) { return this->IsQtCenterNode(item); }
    ) < 2 && "There is at most one center node (zero for most sub-concept maps, one for a complete concept map");
  const auto iter = std::find_if(v.begin(),v.end(),
    [this](const QGraphicsItem * const item) { return this->IsQtCenterNode(item); } );
  assert(iter != v.end());
  const QtNode * const center_node = dynamic_cast<QtNode*>(*iter);
  assert(center_node);
  assert(IsQtCenterNode(center_node));
  return center_node;
}

ribi::cmap::QtNode * ribi::cmap::QtConceptMap::GetCenterNode() noexcept
{
  //Calls the const version of this member function
  //To avoid duplication in const and non-const member functions [1]
  //[1] Scott Meyers. Effective C++ (3rd edition). ISBN: 0-321-33487-6.
  //    Item 3, paragraph 'Avoid duplication in const and non-const member functions'
  QtNode * const qtnode(
    const_cast<QtNode *>(
      dynamic_cast<const QtConceptMap*>(this)->GetCenterNode()
    )
  );
  return qtnode;
}

const ribi::cmap::QtExamplesItem * ribi::cmap::QtConceptMap::GetExamplesItem() const
{
  assert(m_examples_item || !m_examples_item);
  return m_examples_item;
}

ribi::cmap::QtExamplesItem * ribi::cmap::QtConceptMap::GetExamplesItem()
{
  //Calls the const version of this member function
  //To avoid duplication in const and non-const member functions [1]
  //[1] Scott Meyers. Effective C++ (3rd edition). ISBN: 0-321-33487-6.
  //    Item 3, paragraph 'Avoid duplication in const and non-const member functions'
  return const_cast<QtExamplesItem*>(
    const_cast<const QtConceptMap*>(this)->GetExamplesItem());
}

ribi::cmap::QtNode* ribi::cmap::QtConceptMap::GetItemBelowCursor(const QPointF& pos) const
{
  #if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
  const QList<QGraphicsItem*> v = this->scene()->items(pos.x(),pos.y(),2.0,2.0,Qt::IntersectsItemShape,Qt::AscendingOrder);
  #else
  const QList<QGraphicsItem*> v = this->scene()->items(pos.x(),pos.y(),2.0,2.0);
  #endif
  std::vector<QtNode*> qtnodes;
  std::for_each(v.begin(),v.end(),
    [&qtnodes](QGraphicsItem* const item)
    {
      if (QtNode * const qtnode = dynamic_cast<QtNode*>(item))
      {
        assert(!dynamic_cast<QtTool*>(item) && "Cannot draw arrow to ToolsItem");
        qtnodes.push_back(qtnode);
      }
    }
  );
  if (!qtnodes.empty())
  {
    return qtnodes[0];
  }
  return nullptr;
}

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;
}

std::vector<ribi::cmap::QtEdge *> ribi::cmap::QtConceptMap::GetQtEdges()
{
  const std::vector<QtEdge *> qtedges
    = Collect<QtEdge>(this->scene());
  //Cannot do the check below: in DeleteNode multiple edges are deleted
  //assert(qtedges.size() == GetConceptMap()->GetNodes().size()
  //    && "GUI and non-GUI must contain an equal amount of edges");
  return qtedges;
}

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;
}

QGraphicsScene* ribi::cmap::QtConceptMap::GetScene() const noexcept
{

  return scene();
}

std::string ribi::cmap::QtConceptMap::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::cmap::QtConceptMap::GetVersionHistory() noexcept
{
  return {
    "2012-xx-xx: version 1.0: initial version"
    "2013-12-03: version 1.1: start of versioning"
  };
}


bool ribi::cmap::QtConceptMap::IsQtCenterNode(const QGraphicsItem* const item)
{
  const QtCenterNode * const qtnode = dynamic_cast<const QtCenterNode*>(item);
  assert(!qtnode || IsCenterNode(qtnode->GetNode()));
  return qtnode;
}

void ribi::cmap::QtConceptMap::keyPressEvent(QKeyEvent *event) noexcept
{

  switch (event->key())
  {
    case Qt::Key_Equal:
      this->scale(1.1,1.1);
      break;
    case Qt::Key_Minus:
      this->scale(0.9,0.9);
      break;
  }

  QtKeyboardFriendlyGraphicsView::keyPressEvent(event);
  scene()->update();
}

bool ribi::cmap::QtConceptMap::MustReposition(const std::vector<boost::shared_ptr<const cmap::Node> >& nodes) const
{
  //If all are at the origin, the nodes must be (re)positioned
  return std::count_if(nodes.begin(),nodes.end(),
    [](const boost::shared_ptr<const cmap::Node>& node)
    {
      return node->GetX() == 0.0 && node->GetY() == 0.0;
    }
  ) == static_cast<int>(nodes.size());
}

void ribi::cmap::QtConceptMap::OnItemRequestsUpdate(const QGraphicsItem* const item)
{
  OnItemRequestUpdateImpl(item);
}

void ribi::cmap::QtConceptMap::OnRequestSceneUpdate()
{
  scene()->update();
}


void ribi::cmap::QtConceptMap::RepositionItems()
{

  {
    //The ray of the upcoming circle of nodes, is the larger of
    //(1) half of the diagonal of the focal question (e.g. for short concepts)
    //(2) calculated from the circumference by adding the nodes' length
    const std::vector<QtNode *> qtnode_concepts_unsorted = Collect<QtNode>(scene());

    if (qtnode_concepts_unsorted.empty()) return;

    const std::vector<QtNode *> qtnode_concepts = Sort(qtnode_concepts_unsorted);
    assert(!qtnode_concepts.empty());
    assert(!qtnode_concepts.empty());
    assert(qtnode_concepts[0]);
    const QtNode * const qtcenter_node
      = dynamic_cast<const QtNode *>(qtnode_concepts[0]);
    assert(qtcenter_node);
    //assert(qtcenter_node->GetOuterRect(). GetOuterX() > -0.5);
    //assert(qtcenter_node->GetOuterX() <  0.5);
    //assert(qtcenter_node->GetOuterY() > -0.5);
    //assert(qtcenter_node->GetOuterY() <  0.5);

    const double r1
      = 0.5 * ribi::cmap::GetDistance(
        qtcenter_node->boundingRect().width(),
        qtcenter_node->boundingRect().height());
    const double r3 = 50.0;
    const double r = std::max(r1,r3);
    assert(r > 10.0);
    const int n_nodes = qtnode_concepts.size();
    for (int i = 1; i!=n_nodes; ++i) //+1 to skip center node
    {
      //Added +0 (instead of -1) to n_nodes, to prevent, in a setup with two concepts and
      //one edge, the edge to overlap the central question
      const double pi = boost::math::constants::pi<double>();
      const double angle
        = 2.0 * pi * boost::numeric_cast<double>(i)
        / boost::numeric_cast<double>(n_nodes - 1);
      const double x =  std::cos(angle) * r;
      const double y = -std::sin(angle) * r;
      QtNode * const qtnode = qtnode_concepts[i];
      qtnode->GetNode()->SetPos(x,y);
      //qtnode->setPos(x,y);
      #ifndef NDEBUG
      //const double epsilon = 0.000001;
      #endif
      //assert(std::abs(x - qtnode->GetOuterX()) < epsilon);
      //assert(std::abs(x - qtnode->GetNode()->GetX()) < epsilon);
      //assert(std::abs(y - qtnode->GetOuterY()) < epsilon);
      //assert(std::abs(y - qtnode->GetNode()->GetY()) < epsilon);

    }
  }

  #define NOT_NOW_20141111
  #ifdef NOT_NOW_20141111
  {
    //Put the edge concepts in the middle of the nodes
    const std::vector<QtEdge *> qtedge_concepts = Collect<QtEdge>(scene());
    std::for_each(qtedge_concepts.begin(), qtedge_concepts.end(),
      [](QtEdge * const qtedge)
      {
        const QPointF p((qtedge->GetFrom()->GetCenterPos() + qtedge->GetTo()->GetCenterPos()) / 2.0);
        const double new_x = p.x();
        const double new_y = p.y();
        qtedge->GetEdge()->GetNode()->SetX(new_x);
        qtedge->GetEdge()->GetNode()->SetY(new_y);
      }
    );
  }
  #endif // NOT_NOW_20141111

  //Put the nodes around the focal question in their improved position
  //If there is no focal node, the non-focal nodes are put around an empty spot
  for (int i=0; i!=10; ++i) //TODO: replace by while (1)
  {
    bool done = true;
    const std::vector<QtNode *> qtnodes = Sort(Collect<QtNode>(scene()));
    assert(!qtnodes.empty());
    assert(qtnodes[0]);
    //assert(IsCenterNode(qtnodes[0]));
    const std::vector<QtEdge* > qtedges = Collect<QtEdge>(scene());

    //First node
    //const bool is_first_node_center_node {
    //  boost::dynamic_pointer_cast<QtCenterNode>(qtnodes[0])
    //};
    const QtNode * const first_node { qtnodes[0] };
    assert(first_node);

    std::vector<QGraphicsItem*> nodes_and_edges;
    std::copy(qtnodes.begin(),qtnodes.end(),std::back_inserter(nodes_and_edges));
    std::copy(qtedges.begin(),qtedges.end(),std::back_inserter(nodes_and_edges));

    //Move the nodes away from the center
    std::for_each(
      nodes_and_edges.begin() + 1, //+1 to skip the center node at [0]
      nodes_and_edges.end(),
      [first_node,&done](QGraphicsItem* const node_or_edge)
      {
        if (first_node->boundingRect().intersects(
          node_or_edge->boundingRect().translated(-node_or_edge->pos())))
        {
          const double cur_x = node_or_edge->x();
          const double cur_y = node_or_edge->y();
          const double new_x = cur_x + (node_or_edge->x() < first_node->x() ? -1.0 : 1.0);
          const double new_y = cur_y + (node_or_edge->y() < first_node->y() ? -1.0 : 1.0);
          if (QtNode * const qtnode = dynamic_cast<QtNode *>(node_or_edge))
          {
            qtnode->GetNode()->SetX(new_x);
            qtnode->GetNode()->SetY(new_y);
          }
          else
          {
            QtEdge * const qtedge = dynamic_cast<QtEdge *>(node_or_edge);
            assert(qtedge && "Every item is either a Qt node or Qt edge");
            qtedge->GetEdge()->GetNode()->SetX(new_x);
            qtedge->GetEdge()->GetNode()->SetY(new_y);
            //node->setPos(QPointF(new_x,new_y));
          }
          done = false;
        }
      }
    );

    if (done) break;
  }
}

void ribi::cmap::QtConceptMap::SetConceptMap(const boost::shared_ptr<ConceptMap> concept_map)
{
  CleanMe();
  m_concept_map = concept_map;
  if (!m_concept_map) return;

  assert(m_concept_map);
  assert(m_concept_map->IsValid());
  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
  if (!m_concept_map->GetNodes().empty())
  {
    //Add the main question as the first node
    const boost::shared_ptr<Node> node = m_concept_map->GetFocalNode();

    QtNode * qtnode = nullptr;
    if (IsCenterNode(node))
    {
      const boost::shared_ptr<CenterNode> centernode
        = boost::dynamic_pointer_cast<CenterNode>(node);
      qtnode = new QtCenterNode(centernode);
    }
    else
    {
      qtnode = new QtNode(node); //NEVER CALL VIRTUAL FUNCTIONS IN BASE CLASS CONSTRUCTORS!
    }
    assert(qtnode);
    //Let the center node respond to mouse clicks
    //signal #1
    //qtnode->m_signal_request_scene_update.connect(
    //  boost::bind(&ribi::cmap::QtConceptMap::OnRequestSceneUpdate,this)
    //);
    //signal #2
    //qtnode->m_signal_item_has_updated.connect(
    //  boost::bind(&ribi::cmap::QtConceptMap::OnItemRequestsUpdate,this,boost::lambda::_1)
    //);
    //Add the center node to scene
    assert(!qtnode->scene());
    this->scene()->addItem(qtnode);
    qtnodes.push_back(qtnode);
    assert(Collect<QtNode>(scene()).size() == 1);

    //Add the regular nodes to the scene
    const std::vector<boost::shared_ptr<Node>> nodes = m_concept_map->GetNodes();
    const std::size_t n_nodes = nodes.size();
    assert(n_nodes >= 1);
    for (std::size_t i=1; i!=n_nodes; ++i) //+1 to skip focal node
    {
      assert(Collect<QtNode>(scene()).size() == i && "Node not yet added to scene");
      assert(i < nodes.size());
      boost::shared_ptr<Node> node = nodes[i];
      assert(node);
      assert( (IsCenterNode(node) || !IsCenterNode(node))
        && "focal node != center node");
      QtNode * const qtnode = AddNode(node); //NEVER CALL VIRTUAL FUNCTIONS IN BASE CLASS CONSTRUCTORS!
      qtnodes.push_back(qtnode);
      assert(Collect<QtNode>(scene()).size() == i + 1 && "Node is added to scene");
    }
  }
  //Add the Concepts on the Edges
  {
    const std::vector<boost::shared_ptr<ribi::cmap::Edge> > edges = m_concept_map->GetEdges();
    std::for_each(edges.begin(),edges.end(),
      [this,qtnodes](const boost::shared_ptr<Edge> edge)
      {
        assert(edge->GetFrom());
        assert(edge->GetTo());
        assert(edge->GetFrom() != edge->GetTo());
        this->AddEdge(edge); //NEVER CALL VIRTUAL FUNCTIONS IN BASE CLASS CONSTRUCTORS!
      }
    );
  }

  //Put the nodes around the focal question in an initial position
  if (MustReposition(AddConst(m_concept_map->GetNodes())))
  {
    RepositionItems();
  }

  #ifndef NDEBUG
  TestMe(m_concept_map);
  #endif

  assert(GetConceptMap() == concept_map);
  assert( (!concept_map || concept_map->IsValid())
    && "Expect no or a valid concept map");

}

void ribi::cmap::QtConceptMap::SetExamplesItem(QtExamplesItem * const item)
{
  assert((item || !item) && "Can be both");
  m_examples_item = item;
}

#ifndef NDEBUG
void ribi::cmap::QtConceptMap::Shuffle() noexcept
{
  const std::vector<QtNode*> nodes = Collect<QtNode>(scene());
  std::for_each(nodes.begin(),nodes.end(),
    [this](QtNode* qtnode)
    {
      if (!IsQtCenterNode(qtnode))
      {
        #ifdef NOT_NOW_20141111
        double x = qtnode->GetCenterX();
        double y = qtnode->GetCenterY();
        const int i = (std::rand() >> 4) % 4;
        switch(i)
        {
          case 0: x+= 1.0; break;
          case 1: y+= 1.0; break;
          case 2: x+=-1.0; break;
          case 3: y+=-1.0; break;
          default: assert(!"Should not get here");
        }
        assert(QPointF(x,y) != qtnode->GetCenterPos());
        qtnode->GetNode()->SetPos(x,y);
        #endif // NOT_NOW_20141111
      }
    }
  );
  scene()->update();
}
#endif

#ifndef NDEBUG
void ribi::cmap::QtConceptMap::TestMe(const boost::shared_ptr<const ribi::cmap::ConceptMap> map) const
{
  {
    std::set<const cmap::Node*> w;
    const std::vector<boost::shared_ptr<const cmap::Node> > v = map->GetNodes();
    std::transform(v.begin(),v.end(),std::inserter(w,w.begin()),
      [](const boost::shared_ptr<const cmap::Node>  ptr)
      {
        return ptr.get();
      }
    );
    assert(v.size() == w.size() && "All nodes must be unique");
  }
  {
    std::set<const cmap::Edge*> w;
    const std::vector<boost::shared_ptr<const cmap::Edge> > v = map->GetEdges();
    std::transform(v.begin(),v.end(),std::inserter(w,w.begin()),
      [](const boost::shared_ptr<const cmap::Edge>  ptr)
      {
        return ptr.get();
      }
    );
    assert(v.size() == w.size() && "All edges must be unique");
  }
  {
    std::set<QGraphicsItem*> v;
    const std::vector<QtNode*> node_concepts = Collect<QtNode>(scene());
    std::copy(node_concepts.begin(),node_concepts.end(),std::inserter(v,v.begin()));
    const std::vector<QtEdge*> edge_concepts = Collect<QtEdge>(scene());
    std::copy(edge_concepts.begin(),edge_concepts.end(),std::inserter(v,v.begin()));
    assert(v.size() == (node_concepts.size() + edge_concepts.size()) && "All Qt nodes must be unique");
  }
  {
    ///Note that the ConceptMap read out again differs from the original,
    ///because the Nodes are placed
    if (!cmap::ConceptMap::HasSameContent(*GetConceptMap(),*map))
    {
      //OH OH, AN ERROR! HELP ME OUT AND GIMME LOTS OF DEBUG INFO!
      {
        const std::vector<std::string> v
          = xml::XmlToPretty(cmap::ConceptMap::ToXml(map));
        std::clog << "original map:\n";
        std::clog << "\n";
        std::copy(v.begin(),v.end(),std::ostream_iterator<std::string>(std::clog,"\n"));
        std::clog << "\n";
      }
      {
        const std::vector<std::string> v
          = xml::XmlToPretty(cmap::ConceptMap::ToXml(GetConceptMap()));
        std::clog << "GetConceptMap():\n";
        std::clog << "\n";
        std::copy(v.begin(),v.end(),std::ostream_iterator<std::string>(std::clog,"\n"));
        std::clog << "\n";
      }

      TRACE(cmap::ConceptMap::ToXml(GetConceptMap()));
      TRACE(cmap::ConceptMap::ToXml(map));
    }
  }
  assert(cmap::ConceptMap::HasSameContent(*GetConceptMap(),*map)
    && "The concept map supplied must be homomorphous to the one created in the widget");

}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmapbrating.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmaprating.h"

#include <cassert>
#include <boost/numeric/conversion/cast.hpp>

#include "conceptmap.h"
#include "conceptmapnode.h"
#include "conceptmapconcept.h"
#include "conceptmapexamples.h"
#pragma GCC diagnostic pop

int ribi::cmap::Rating::SuggestComplexity(const int n_edges, const int n_examples)
{
  const int complexity
    = n_edges == 0  || (n_edges == 1 && n_examples == 0)
    ? 0
    : (n_edges == 1 && n_examples > 0) || (n_edges == 2 && n_examples == 0)
      ? 1
      : 2;
  return complexity;
}

int ribi::cmap::Rating::SuggestComplexity(const boost::shared_ptr<const ribi::cmap::ConceptMap> sub_concept_map)
{
  assert(sub_concept_map);
  const int n_edges = boost::numeric_cast<int>(sub_concept_map->GetEdges().size());
  assert(!sub_concept_map->GetNodes().empty());
  assert(sub_concept_map->GetFocalNode()->GetConcept());
  assert(sub_concept_map->GetFocalNode()->GetConcept()->GetExamples());
  const int n_examples
    = boost::numeric_cast<int>(
      sub_concept_map->GetFocalNode()->GetConcept()->GetExamples()->Get().size()
    );
  return SuggestComplexity(n_edges,n_examples);
}

int ribi::cmap::Rating::SuggestConcreteness(const int n_examples)
{
  const int concreteness
    = n_examples < 2
    ? 0
    : n_examples > 1 && n_examples < 4
      ? 1
      : 2;
  return concreteness;
}

int ribi::cmap::Rating::SuggestConcreteness(const boost::shared_ptr<const ribi::cmap::ConceptMap> sub_concept_map)
{
  assert(sub_concept_map);
  assert(!sub_concept_map->GetNodes().empty());
  assert(sub_concept_map->GetFocalNode()->GetConcept());
  assert(sub_concept_map->GetFocalNode()->GetConcept()->GetExamples());
  const int n_examples
    = boost::numeric_cast<int>(
      sub_concept_map->GetFocalNode()->GetConcept()->GetExamples()->Get().size()
    );
  return SuggestConcreteness(n_examples);
}

int ribi::cmap::Rating::SuggestSpecificity(const int n_examples)
{
  const int specificity = SuggestConcreteness(n_examples);
  return specificity;
}

int ribi::cmap::Rating::SuggestSpecificity(const boost::shared_ptr<const ribi::cmap::ConceptMap> sub_concept_map)
{
  assert(sub_concept_map);
  assert(!sub_concept_map->GetNodes().empty());
  assert(sub_concept_map->GetFocalNode()->GetConcept());
  assert(sub_concept_map->GetFocalNode()->GetConcept()->GetExamples());
  const int n_examples
    = boost::numeric_cast<int>(
      sub_concept_map->GetFocalNode()->GetConcept()->GetExamples()->Get().size()
    );
  return SuggestSpecificity(n_examples);
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapbrushfactory.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPBRUSHFACTORY_H
#define QTCONCEPTMAPBRUSHFACTORY_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <QBrush>
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

///Class for holding some QBrush instances
struct QtBrushFactory
{
  ///Obtain a brush its name
  static std::string BrushToStr(const QBrush& brush);

  ///Blue brush, to be used in all derived classes for uniform coloring
  static QBrush CreateBlueGradientBrush() noexcept;

  ///Gold brush, to be used in all derived classes for uniform coloring
  static QBrush CreateGoldGradientBrush() noexcept;

  ///Gray brush, to be used in all derived classes for uniform coloring
  static QBrush CreateGrayGradientBrush() noexcept;

  ///Green brush, to be used in all derived classes for uniform coloring
  static QBrush CreateGreenGradientBrush() noexcept;

  ///Red brush, to be used in all derived classes for uniform coloring
  static QBrush CreateRedGradientBrush() noexcept;

  ///Yellow brush, to be used in all derived classes for uniform coloring
  static QBrush CreateYellowGradientBrush() noexcept;

  ///Near-white brush, to be used in all derived classes for uniform coloring
  static QBrush CreateWhiteGradientBrush() noexcept;

  private:
  ///The bottom coordinat of a gradient
  static const int sm_bottom;
  ///The left coordinat of a gradient
  static const int sm_left;
  ///The right coordinat of a gradient
  static const int sm_right;
  ///The top coordinat of a gradient
  static const int sm_top;
  ///The low color value: low values return in darker brushes
  static const int sm_color_low_value;
  ///The middle color value: higher values return in lighter brushes
  static const int sm_color_mid_value;
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPBRUSHFACTORY_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapbrushfactory.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapbrushfactory.h"

#include <QLinearGradient>
#pragma GCC diagnostic pop

const int ribi::cmap::QtBrushFactory::sm_bottom = 10;
const int ribi::cmap::QtBrushFactory::sm_left = -100;
const int ribi::cmap::QtBrushFactory::sm_right = 100;
const int ribi::cmap::QtBrushFactory::sm_top = -10;
const int ribi::cmap::QtBrushFactory::sm_color_low_value  =  64;
const int ribi::cmap::QtBrushFactory::sm_color_mid_value  = 128;

std::string ribi::cmap::QtBrushFactory::BrushToStr(const QBrush& brush)
{
  if (brush == CreateBlueGradientBrush()) return "blue";
  if (brush == CreateGoldGradientBrush()) return "gold";
  if (brush == CreateGrayGradientBrush()) return "gray";
  if (brush == CreateGreenGradientBrush()) return "green";
  if (brush == CreateRedGradientBrush()) return "red";
  if (brush == CreateYellowGradientBrush()) return "yellow";
  if (brush == CreateWhiteGradientBrush()) return "white";
  return "unknown";

}

QBrush ribi::cmap::QtBrushFactory::CreateBlueGradientBrush() noexcept
{
  QLinearGradient linearGradient(sm_left,sm_top,sm_right,sm_bottom);
  linearGradient.setColorAt(0.0,QColor( sm_color_mid_value, sm_color_mid_value,255));
  linearGradient.setColorAt(1.0,QColor(255,255,255));
  return linearGradient;
}

QBrush ribi::cmap::QtBrushFactory::CreateGoldGradientBrush() noexcept
{
  QLinearGradient linearGradient(sm_left,sm_top,sm_right,sm_bottom);
  linearGradient.setColorAt(0.0,QColor( sm_color_mid_value, sm_color_mid_value,sm_color_low_value));
  linearGradient.setColorAt(1.0,QColor(255,255,sm_color_low_value));
  return linearGradient;
}

QBrush ribi::cmap::QtBrushFactory::CreateGrayGradientBrush() noexcept
{
  QLinearGradient linearGradient(sm_left,sm_top,sm_right,sm_bottom);
  linearGradient.setColorAt(0.0,QColor(196,196,196));
  linearGradient.setColorAt(1.0,QColor(255,255,255));
  return linearGradient;
}

QBrush ribi::cmap::QtBrushFactory::CreateGreenGradientBrush() noexcept
{
  QLinearGradient linearGradient(sm_left,sm_top,sm_right,sm_bottom);
  linearGradient.setColorAt(0.0,QColor( sm_color_mid_value,255, sm_color_mid_value));
  linearGradient.setColorAt(1.0,QColor(255,255,255));
  return linearGradient;
}

QBrush ribi::cmap::QtBrushFactory::CreateRedGradientBrush() noexcept
{
  QLinearGradient linearGradient(sm_left,sm_top,sm_right,sm_bottom);
  linearGradient.setColorAt(0.0,QColor(255, sm_color_mid_value, sm_color_mid_value));
  linearGradient.setColorAt(1.0,QColor(255,255,255));
  return linearGradient;
}

QBrush ribi::cmap::QtBrushFactory::CreateYellowGradientBrush() noexcept
{
  QLinearGradient linearGradient(sm_left,sm_top,sm_right,sm_bottom);
  linearGradient.setColorAt(0.0,QColor(255,255, sm_color_mid_value));
  linearGradient.setColorAt(1.0,QColor(255,255,255));
  return linearGradient;
}

QBrush ribi::cmap::QtBrushFactory::CreateWhiteGradientBrush() noexcept
{
  QLinearGradient linearGradient(sm_left,sm_top,sm_right,sm_bottom);
  const int low_value = 250;
  linearGradient.setColorAt(0.0,QColor(low_value,low_value,low_value));
  linearGradient.setColorAt(1.0,QColor(255,255,255));
  return linearGradient;
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapcenternode.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2014 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPCENTERNODE_H
#define QTCONCEPTMAPCENTERNODE_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qtconceptmapqtnode.h"

#include "conceptmapfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

///The center node
struct QtCenterNode : public QtNode
{
  ///Node cannot be const, as it contains a Concept the user might want to edit
  ///(that is, when a sub-concept map is created from a concept map and the
  ///focal node needs to be rated)
  ///concept_item is the display Strategy
  explicit QtCenterNode(const boost::shared_ptr<CenterNode> node);

  private:
  //static const boost::shared_ptr<QtItemDisplayStrategy> CreateConceptItem(const boost::shared_ptr<Node> node);
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPCENTERNODE_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapcenternode.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapcenternode.h"

#include "conceptmapcenternode.h"
#include "qtitemdisplaystrategy.h"
#include "qtconceptmapbrushfactory.h"
#include "conceptmapnode.h"
#include "qtconceptmapdisplaystrategy.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::cmap::QtCenterNode::QtCenterNode(
  const boost::shared_ptr<CenterNode> node
)
  : QtNode(node)
{
  assert(node);
  //assert(this->GetDisplayStrategy());
  this->setFlags(QGraphicsItem::ItemIsFocusable);
  //this->GetDisplayStrategy()->SetMainBrush(QtBrushFactory::CreateGoldGradientBrush());
}

/*
const boost::shared_ptr<ribi::cmap::QtItemDisplayStrategy> ribi::cmap::QtCenterNode::CreateConceptItem(
  const boost::shared_ptr<Node> node)
{
  assert(node);
  const boost::shared_ptr<QtDisplayStrategy> item(new QtDisplayStrategy(node->GetConcept()));
  assert(item);
  return item;
}
*/

 

 

 

 

 

./CppQtConceptMap/qtconceptmapcollect.h

 

#ifndef QTCONCEPTMAPCOLLECT_H
#define QTCONCEPTMAPCOLLECT_H

#include "qtconceptmapqtnode.h"

///Collect all QGraphicsItems with class T in an unorderly way
template <class T>
std::vector<T*> Collect(const QGraphicsScene* const scene)
{
  std::vector<T*> v;
  const QList<QGraphicsItem *> items = scene->items();
  std::transform(items.begin(),items.end(),std::back_inserter(v),
    [](QGraphicsItem* const item)
    {
      return dynamic_cast<T*>(item);
    }
  );
  v.erase(std::remove(v.begin(),v.end(),nullptr),v.end());
  assert(std::count(v.begin(),v.end(),nullptr)==0);
  return v;
}

#endif // QTCONCEPTMAPCOLLECT_CPP

 

 

 

 

 

./CppQtConceptMap/qtconceptmapcompetency.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPCOMPETENCY_H
#define QTCONCEPTMAPCOMPETENCY_H

#include <map>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <QColor>
#include <QIcon>
#include "conceptmapcompetency.h"

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

///Class to convert cmap::Competency to QColor and vice versa
struct QtCompetency
{
  QtCompetency();

  ///Convert a QColor to a cmap::Competency
  cmap::Competency ColorToCompetency(const QColor& color) const;

  ///Convert a cmap::Competency to a QColor
  QColor CompetencyToColor(const cmap::Competency competency) const;

  ///Convert a cmap::Competency to a QIcon
  QIcon CompetencyToIcon(const cmap::Competency competency) const;

  ///Convert a QIcon to a cmap::Competency
  cmap::Competency IconToCompetency(const QIcon& icon) const;

  private:
  ///The map between cmap::Competency and QColor
  static const std::map<cmap::Competency,QColor> m_color_map;

  ///The map between cmap::Competency and QIcon
  static std::map<cmap::Competency,QIcon> m_icon_map;

  ///Create map between cmap::Competency and QColor
  static const std::map<cmap::Competency,QColor> CreateColorMap();

  ///Create map between cmap::Competency and QIcon
  static const std::map<cmap::Competency,QIcon> CreateIconMap();

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPCOMPETENCY_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapcompetency.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapcompetency.h"

#include <cassert>

#include "conceptmapcompetencies.h"

#include <QImage>
#include <QPixmap>
#include "testtimer.h"
#include "trace.h"
#pragma GCC diagnostic pop

const std::map<ribi::cmap::Competency,QColor> ribi::cmap::QtCompetency::m_color_map = ribi::cmap::QtCompetency::CreateColorMap();
//const std::map<cmap::Competency,QIcon > ribi::cmap::QtCompetency::m_icon_map  = ribi::cmap::QtCompetency::CreateIconMap();
std::map<ribi::cmap::Competency,QIcon > ribi::cmap::QtCompetency::m_icon_map;

ribi::cmap::QtCompetency::QtCompetency()
{
  #ifndef NDEBUG
  Test();
  #endif
}

ribi::cmap::Competency ribi::cmap::QtCompetency::ColorToCompetency(const QColor& color) const
{
  const auto iter = std::find_if(m_color_map.begin(),m_color_map.end(),
    [color](const std::pair<cmap::Competency,QColor>& p)
    {
      return
           color.red() == p.second.red()
        && color.green() == p.second.green()
        && color.blue() == p.second.blue();
    }
  );
  assert(iter!=m_color_map.end());
  return iter->first;
}

QColor ribi::cmap::QtCompetency::CompetencyToColor(const cmap::Competency competency) const
{
  const auto iter = m_color_map.find(competency);
  assert(iter!=m_color_map.end());
  return iter->second;
}

QIcon ribi::cmap::QtCompetency::CompetencyToIcon(const cmap::Competency competency) const
{
  if (m_icon_map.empty()) m_icon_map = CreateIconMap();
  assert(!m_icon_map.empty());
  const auto iter = m_icon_map.find(competency);
  assert(iter!=m_icon_map.end());
  QIcon icon = iter->second;
  assert(!icon.isNull());
  return icon;
}

const std::map<ribi::cmap::Competency,QColor> ribi::cmap::QtCompetency::CreateColorMap()
{
  return
  {
    { cmap::Competency::uninitialized      , Qt::white },
    { cmap::Competency::profession         , Qt::red },
    { cmap::Competency::organisations      , QColor(255,127,0) }, //Orange
    { cmap::Competency::social_surroundings, Qt::yellow },
    { cmap::Competency::target_audience    , Qt::green },
    { cmap::Competency::ti_knowledge       , Qt::cyan },
    { cmap::Competency::prof_growth        , Qt::blue },
    { cmap::Competency::misc               , Qt::magenta }
  };
}

const std::map<ribi::cmap::Competency,QIcon> ribi::cmap::QtCompetency::CreateIconMap()
{
  return
  {
    /*
    { cmap::Competency::uninitialized      , QIcon(":/images/PicWhite14x14.png") },
    { cmap::Competency::profession         , QIcon(":/images/PicPurple14x14.png") },
    { cmap::Competency::organisations      , QIcon(":/images/PicBlue14x14.png") },
    { cmap::Competency::social_surroundings, QIcon(":/images/PicCyan14x14.png") },
    { cmap::Competency::target_audience    , QIcon(":/images/PicGreen14x14.png") },
    { cmap::Competency::ti_knowledge       , QIcon(":/images/PicYelow14x14.png") },
    { cmap::Competency::prof_growth        , QIcon(":/images/PicOrange14x14.png") },
    { cmap::Competency::misc               , QIcon(":/images/PicRed14x14.png") }
    */
    { cmap::Competency::uninitialized      , QIcon(":/images/PicWhite14x14.png") },
    { cmap::Competency::profession         , QIcon(":/images/PicRed14x14.png") },
    { cmap::Competency::organisations      , QIcon(":/images/PicOrange14x14.png") },
    { cmap::Competency::social_surroundings, QIcon(":/images/PicYellow14x14.png") },
    { cmap::Competency::target_audience    , QIcon(":/images/PicGreen14x14.png") },
    { cmap::Competency::ti_knowledge       , QIcon(":/images/PicCyan14x14.png") },
    { cmap::Competency::prof_growth        , QIcon(":/images/PicBlue14x14.png") },
    { cmap::Competency::misc               , QIcon(":/images/PicPurple14x14.png") }
  };
}

ribi::cmap::Competency ribi::cmap::QtCompetency::IconToCompetency(const QIcon& icon) const
{
  #ifndef NDEBUG
  Test();
  #endif
  if (m_icon_map.empty()) m_icon_map = CreateIconMap();
  assert(!m_icon_map.empty());
  const auto iter = std::find_if(m_icon_map.begin(),m_icon_map.end(),
    [icon](const std::pair<cmap::Competency,QIcon>& p)
    {

      return icon.pixmap(14,14).toImage() == (p.second).pixmap(14,14).toImage();
      //return icon.name() == (p.second).name();
      //assert(icon.pixmap().size() == (p.second).pixmap().size());
      //return icon.pixmap() == (p.second).pixmap();
    }
  );
  assert(iter!=m_icon_map.end());
  return iter->first;
}

#ifndef NDEBUG
void ribi::cmap::QtCompetency::Test() noexcept
{
  {
    static bool is_tested = false;
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer{__func__,__FILE__,0.1};
  //Conversion between QColor and cmap::Competency
  {
    const std::vector<cmap::Competency> v = Competencies().GetAllCompetencies();
    std::for_each(v.begin(),v.end(),
      [](const cmap::Competency& competency)
      {
        QColor color = ribi::cmap::QtCompetency().CompetencyToColor(competency);
        assert(ribi::cmap::QtCompetency().ColorToCompetency(color) == competency);
      }
    );
  }
  //Conversion between QIcon and cmap::Competency
  {
    const std::vector<cmap::Competency> v = Competencies().GetAllCompetencies();
    std::for_each(v.begin(),v.end(),
      [](const cmap::Competency& competency)
      {
        QIcon icon = ribi::cmap::QtCompetency().CompetencyToIcon(competency);
        assert(ribi::cmap::QtCompetency().IconToCompetency(icon) == competency);
      }
    );
  }

}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmapconceptdialog.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPCONCEPTDIALOG_H
#define QTCONCEPTMAPCONCEPTDIALOG_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace Ui { class QtConceptDialog; }

namespace ribi {
namespace cmap {

///Displays and modifies a Concept
class QtConceptDialog : public ribi::QtHideAndShowDialog
{
  Q_OBJECT

public:
  explicit QtConceptDialog(QWidget *parent = 0);
  QtConceptDialog(const QtConceptDialog&) = delete;
  QtConceptDialog& operator=(const QtConceptDialog&) = delete;
  ~QtConceptDialog() noexcept;

  boost::shared_ptr<Concept> GetConcept() const noexcept { return m_concept; }
  static int GetMinimumHeight(const Concept& concept) noexcept;
  std::string GetUiName() const noexcept;

  void SetConcept(const boost::shared_ptr<Concept>& concept) noexcept;
  void SetUiName(const std::string& name) noexcept;


private slots:
  void on_box_is_complex_stateChanged(int arg1) noexcept;
  void on_box_rating_complexity_valueChanged(int arg1) noexcept;
  void on_box_rating_concreteness_valueChanged(int arg1) noexcept;
  void on_box_rating_specificity_valueChanged(int arg1) noexcept;
  void on_edit_name_textChanged(const QString &arg1) noexcept;

private:
  Ui::QtConceptDialog *ui;

  ///The Concept to work on
  boost::shared_ptr<Concept> m_concept;

  boost::shared_ptr<QtExamplesDialog> m_qtexamplesdialog;

  //concept is non-const, as its displayal by this dialog renders it editable
  void OnExamplesChanged(Concept * const concept) noexcept;

  //concept is non-const, as its displayal by this dialog renders it editable
  void OnIsComplexChanged(Concept * const concept) noexcept;

  //concept is non-const, as its displayal by this dialog renders it editable
  void OnNameChanged(Concept * const concept) noexcept;

  //concept is non-const, as its displayal by this dialog renders it editable
  void OnRatingComplexityChanged(Concept * const concept) noexcept;

  //concept is non-const, as its displayal by this dialog renders it editable
  void OnRatingConcretenessChanged(Concept * const concept) noexcept;

  //concept is non-const, as its displayal by this dialog renders it editable
  void OnRatingSpecificityChanged(Concept * const concept) noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPCONCEPTDIALOG_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapconceptdialog.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapconceptdialog.h"

#include <cassert>
#include <boost/bind/bind.hpp>
#include <boost/lambda/lambda.hpp>

#include "conceptmapcompetencies.h"
#include "conceptmapconcept.h"
#include "conceptmapconceptfactory.h"
#include "conceptmapexamples.h"
#include "conceptmapexamplesfactory.h"
#include "qtconceptmapexamplesdialog.h"
#include "testtimer.h"
#include "trace.h"
#include "ui_qtconceptmapconceptdialog.h"

#pragma GCC diagnostic pop

ribi::cmap::QtConceptDialog::QtConceptDialog(QWidget *parent) :
  ribi::QtHideAndShowDialog(parent),
  ui(new Ui::QtConceptDialog),
  m_concept{},
  m_qtexamplesdialog{new QtExamplesDialog}
{
  ui->setupUi(this);
  #ifndef NDEBUG
  Test();
  #endif

  {
    assert(layout());
    layout()->addWidget(m_qtexamplesdialog.get());

  }

  const auto concept
    = ConceptFactory().Create("QtConceptDialog initial concept",
        ExamplesFactory().GetTest(2),true,-1,-1,-1);
  this->SetConcept(concept);
}

ribi::cmap::QtConceptDialog::~QtConceptDialog() noexcept
{
  delete ui;
}

int ribi::cmap::QtConceptDialog::GetMinimumHeight(const Concept& concept) noexcept
{
  return
      QtExamplesDialog::GetMinimumHeight(*concept.GetExamples())
    + 197
  ;
}

std::string ribi::cmap::QtConceptDialog::GetUiName() const noexcept
{
  return ui->edit_name->text().toStdString();
}

void ribi::cmap::QtConceptDialog::SetConcept(const boost::shared_ptr<Concept>& concept) noexcept
{
  const bool verbose{false};

  assert(concept);
  if (m_concept == concept)
  {
    return;
  }
  if (verbose)
  {
    std::stringstream s;
    s << "Setting concept '" << concept->ToStr() << "'\n";
  }
  const auto examples_after = concept->GetExamples();
  const auto is_complex_after = concept->GetIsComplex();
  const auto name_after = concept->GetName();
  const auto rating_complexity_after = concept->GetRatingComplexity();
  const auto rating_concreteness_after = concept->GetRatingConcreteness();
  const auto rating_specificity_after = concept->GetRatingSpecificity();

  bool examples_changed  = true;
  bool is_complex_changed  = true;
  bool name_changed = true;
  bool rating_complexity_changed = true;
  bool rating_concreteness_changed = true;
  bool rating_specificity_changed = true;

  if (m_concept)
  {
    const auto examples_before = m_concept->GetExamples();
    const auto is_complex_before = m_concept->GetIsComplex();
    const auto name_before = m_concept->GetName();
    const auto rating_complexity_before = m_concept->GetRatingComplexity();
    const auto rating_concreteness_before = m_concept->GetRatingConcreteness();
    const auto rating_specificity_before = m_concept->GetRatingSpecificity();

    examples_changed  = examples_before != examples_after;
    is_complex_changed  = is_complex_before != is_complex_after;
    name_changed = name_before != name_after;
    rating_complexity_changed = rating_complexity_before != rating_complexity_after;
    rating_concreteness_changed = rating_concreteness_before != rating_concreteness_after;
    rating_specificity_changed = rating_specificity_before != rating_specificity_after;


    if (verbose)
    {
      if (examples_changed)
      {
        std::stringstream s;
        s
          << "Examples will change from "
          << examples_before->ToStr()
          << " to "
          << examples_after->ToStr()
          << '\n'
        ;
        TRACE(s.str());
      }
      if (is_complex_changed)
      {
        std::stringstream s;
        s << "IsComplex will change from " << is_complex_before
          << " to " << is_complex_after << '\n';
        TRACE(s.str());
      }
      if (name_changed)
      {
        std::stringstream s;
        s << "Name will change from " << name_before
          << " to " << name_after << '\n';
        TRACE(s.str());
      }
      if (rating_complexity_changed)
      {
        std::stringstream s;
        s << "Rating_complexity will change from " << rating_complexity_before
          << " to " << rating_complexity_after << '\n';
        TRACE(s.str());
      }
      if (rating_concreteness_changed)
      {
        std::stringstream s;
        s << "Rating_concreteness_changed will change from '"
          << rating_concreteness_before
          << "' to '" << rating_concreteness_after
          << "'\n";
        TRACE(s.str());
      }
      if (rating_specificity_changed)
      {
        std::stringstream s;
        s << "Rating_specificity will change from '" << rating_specificity_before
          << "' to '" << rating_specificity_after << "'\n";
        TRACE(s.str());
      }
    }
    //Disconnect m_concept
    m_concept->m_signal_examples_changed.disconnect(
      boost::bind(&ribi::cmap::QtConceptDialog::OnExamplesChanged,this,boost::lambda::_1)
    );
    m_concept->m_signal_is_complex_changed.disconnect(
      boost::bind(&ribi::cmap::QtConceptDialog::OnIsComplexChanged,this,boost::lambda::_1)
    );
    m_concept->m_signal_name_changed.disconnect(
      boost::bind(&ribi::cmap::QtConceptDialog::OnNameChanged,this,boost::lambda::_1)
    );
    m_concept->m_signal_rating_complexity_changed.disconnect(
      boost::bind(&ribi::cmap::QtConceptDialog::OnRatingComplexityChanged,this,boost::lambda::_1)
    );
    m_concept->m_signal_rating_concreteness_changed.disconnect(
      boost::bind(&ribi::cmap::QtConceptDialog::OnRatingConcretenessChanged,this,boost::lambda::_1)
    );
    m_concept->m_signal_rating_specificity_changed.disconnect(
      boost::bind(&ribi::cmap::QtConceptDialog::OnRatingSpecificityChanged,this,boost::lambda::_1)
    );
  }

  //Replace m_example by the new one
  m_concept = concept;

  assert(m_concept->GetExamples() == examples_after );
  assert(m_concept->GetIsComplex()  == is_complex_after );
  assert(m_concept->GetName() == name_after);
  assert(m_concept->GetRatingComplexity() == rating_complexity_after);
  assert(m_concept->GetRatingConcreteness() == rating_concreteness_after);
  assert(m_concept->GetRatingSpecificity() == rating_specificity_after);

  m_concept->m_signal_examples_changed.connect(
    boost::bind(&ribi::cmap::QtConceptDialog::OnExamplesChanged,this,boost::lambda::_1)
  );
  m_concept->m_signal_is_complex_changed.connect(
    boost::bind(&ribi::cmap::QtConceptDialog::OnIsComplexChanged,this,boost::lambda::_1)
  );
  m_concept->m_signal_name_changed.connect(
    boost::bind(&ribi::cmap::QtConceptDialog::OnNameChanged,this,boost::lambda::_1)
  );
  m_concept->m_signal_rating_complexity_changed.connect(
    boost::bind(&ribi::cmap::QtConceptDialog::OnRatingComplexityChanged,this,boost::lambda::_1)
  );
  m_concept->m_signal_rating_concreteness_changed.connect(
    boost::bind(&ribi::cmap::QtConceptDialog::OnRatingConcretenessChanged,this,boost::lambda::_1)
  );
  m_concept->m_signal_rating_specificity_changed.connect(
    boost::bind(&ribi::cmap::QtConceptDialog::OnRatingSpecificityChanged,this,boost::lambda::_1)
  );

  //Emit everything that has changed
  if (examples_changed)
  {
    m_concept->m_signal_examples_changed(m_concept.get());
  }
  if (is_complex_changed)
  {
    m_concept->m_signal_is_complex_changed(m_concept.get());
  }
  if (name_changed)
  {
    m_concept->m_signal_name_changed(m_concept.get());
  }
  if (rating_complexity_changed)
  {
    m_concept->m_signal_rating_complexity_changed(m_concept.get());
  }
  if (rating_concreteness_changed)
  {
    m_concept->m_signal_rating_concreteness_changed(m_concept.get());
  }
  if (rating_specificity_changed)
  {
    m_concept->m_signal_rating_specificity_changed(m_concept.get());
  }

  setMinimumHeight(GetMinimumHeight(*m_concept));

  assert( concept ==  m_concept);
  assert(*concept == *m_concept);
}

void ribi::cmap::QtConceptDialog::OnExamplesChanged(Concept * const concept) noexcept
{
  const bool verbose{false};
  assert(concept);

  const auto examples_before = m_qtexamplesdialog->GetExamples();
  const boost::shared_ptr<Examples> examples_after = concept->GetExamples();

  if (verbose)
  {
    std::stringstream s;
    s << "Change examples from "
    << (examples_before ? examples_before->ToStr() : "[NONE]")
    << " to " << examples_after->ToStr();
    TRACE(s.str());
  }

  m_qtexamplesdialog->SetExamples(examples_after);

  assert(m_qtexamplesdialog->GetExamples() == examples_after);
}

void ribi::cmap::QtConceptDialog::OnIsComplexChanged(Concept * const concept) noexcept
{
  assert(concept);
  ui->box_is_complex->setChecked(concept->GetIsComplex());
}

void ribi::cmap::QtConceptDialog::OnNameChanged(Concept * const concept) noexcept
{
  assert(concept);
  ui->edit_name->setText(concept->GetName().c_str());
}

void ribi::cmap::QtConceptDialog::OnRatingComplexityChanged(Concept * const concept) noexcept
{
  assert(concept);
  ui->box_rating_complexity->setValue(
    concept->GetRatingComplexity()
  );
}

void ribi::cmap::QtConceptDialog::OnRatingConcretenessChanged(Concept * const concept) noexcept
{
  assert(concept);
  ui->box_rating_concreteness->setValue(
    concept->GetRatingConcreteness()
  );
}

void ribi::cmap::QtConceptDialog::OnRatingSpecificityChanged(Concept * const concept) noexcept
{
  assert(concept);
  ui->box_rating_specificity->setValue(
    concept->GetRatingSpecificity()
  );
}

void ribi::cmap::QtConceptDialog::SetUiName(const std::string& name) noexcept
{
  ui->edit_name->setText(name.c_str());
  assert(GetUiName() == name);
}


#ifndef NDEBUG
void ribi::cmap::QtConceptDialog::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  QtExamplesDialog();
  const TestTimer test_timer(__func__,__FILE__,1.0);
  QtConceptDialog d;
}
#endif

/*
void ribi::cmap::QtConceptDialog::on_box_competency_currentIndexChanged(int index)
{
  const bool verbose{false};

  if (!m_concept)
  {
    //Used in construction
    return;
  }

  assert(index >= 0);
  assert(index < static_cast<int>(Competencies().GetAllCompetencies().size()));
  const auto competency = Competencies().GetAllCompetencies()[index];

  if (verbose)
  {
    std::stringstream s;
    s << "QtConceptDialog will set competency " << Competencies().ToStr(competency)
      << " (index " << index << ", current competency is "
      << (m_concept ? Competencies().ToStr(m_concept->GetCompetency()) : "[nullptr]")
      << ")";
    TRACE(s.str());
  }
  //Let the Concept figure out itself if this changes anything;
  //Allow setting a new competency if it equals the current
  m_concept->SetCompetency(competency);

  assert(m_concept->GetCompetency() == competency);
}
*/

void ribi::cmap::QtConceptDialog::on_box_is_complex_stateChanged(int) noexcept
{
  //OK
  m_concept->SetIsComplex(ui->box_is_complex->isChecked());
}

void ribi::cmap::QtConceptDialog::on_edit_name_textChanged(const QString &arg1) noexcept
{
  //OK
  m_concept->SetName(arg1.toStdString());
}

void ribi::cmap::QtConceptDialog::on_box_rating_complexity_valueChanged(int arg1) noexcept
{
  m_concept->SetRatingComplexity(arg1);
}

void ribi::cmap::QtConceptDialog::on_box_rating_concreteness_valueChanged(int arg1) noexcept
{
  m_concept->SetRatingConcreteness(arg1);
}

void ribi::cmap::QtConceptDialog::on_box_rating_specificity_valueChanged(int arg1) noexcept
{
  m_concept->SetRatingSpecificity(arg1);
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapconcepteditdialog.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPCONCEPTEDITDIALOG_H
#define QTCONCEPTMAPCONCEPTEDITDIALOG_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"
#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

struct QListWidgetItem;

namespace Ui { class QtConceptMapConceptEditDialog; }

namespace ribi {
namespace cmap {

class QtConceptMapConceptEditDialog : public ribi::QtHideAndShowDialog
{
  Q_OBJECT
  
public:
  ///concept is not const as user might want to modify it
  ///concept is only modified if user clicks OK
  explicit QtConceptMapConceptEditDialog(
    const boost::shared_ptr<Concept> concept,
    QWidget* parent = 0);
  QtConceptMapConceptEditDialog(const QtConceptMapConceptEditDialog&) = delete;
  QtConceptMapConceptEditDialog& operator=(const QtConceptMapConceptEditDialog&) = delete;
  ~QtConceptMapConceptEditDialog() noexcept;

protected:
  void keyPressEvent(QKeyEvent *);

private slots:
  void RemoveEmptyItem(QListWidgetItem * item);

  void on_button_add_clicked();

  ///Finally convert what the GUI displays to a Concept
  void on_button_ok_clicked();

private:
  Ui::QtConceptMapConceptEditDialog *ui;


#ifdef CONCEPTMAP_WRITE_TO_CONCEPT
  const int m_rating_complexity;
  const int m_rating_concreteness;
  const int m_rating_specificity;
#else
  ///The concept being modified, cannot be const
  const boost::shared_ptr<Concept> m_concept;

#endif

  ///Test this class
  static void Test() noexcept;
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPCONCEPTEDITDIALOG_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapconcepteditdialog.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapconcepteditdialog.h"

#include <cassert>

#include <QKeyEvent>
#include <QObjectList>

#include "conceptmapcompetency.h"
#include "conceptmapexample.h"
#include "conceptmapexamplefactory.h"
#include "conceptmapexamplesfactory.h"
#include "conceptmapexamples.h"
#include "conceptmaphelper.h"
#include "conceptmapconcept.h"
#include "conceptmapconceptfactory.h"
#include "qtconceptmapcompetency.h"
#include "ui_qtconceptmapconcepteditdialog.h"
#include "testtimer.h"
#include "trace.h"
#pragma GCC diagnostic pop

///QTreeWidgetItem with the only function of storing a cmap::Competency additionally, only used in testing
struct QtConceptMapListWidgetItem : public QListWidgetItem
{
  QtConceptMapListWidgetItem(
    const ribi::cmap::Competency competency
    )
    : QListWidgetItem(0),
      m_competency(competency)
  {

  }
  const ribi::cmap::Competency m_competency;
};

ribi::cmap::QtConceptMapConceptEditDialog::QtConceptMapConceptEditDialog(
  const boost::shared_ptr<Concept> concept,
  QWidget* parent)
  : QtHideAndShowDialog(parent),
    ui(new Ui::QtConceptMapConceptEditDialog),
#ifdef CONCEPTMAP_WRITE_TO_CONCEPT
    m_rating_complexity(concept->GetRatingComplexity()),
    m_rating_concreteness(concept->GetRatingConcreteness()),
    m_rating_specificity(concept->GetRatingSpecificity())
#else
    m_concept(concept)
#endif
{
  ui->setupUi(this);
  #ifndef NDEBUG
  Test();
  assert(concept);
  #endif

  //Convert the concept to its GUI elements
  //Add the name
  ui->edit_concept->setText(concept->GetName().c_str());
  //Add the examples
  const std::vector<boost::shared_ptr<const cmap::Example> > v = AddConst(concept->GetExamples()->Get());
  std::for_each(v.begin(),v.end(),
    [this](const boost::shared_ptr<const cmap::Example>& s)
    {
      assert(!s->GetText().empty());
      QtConceptMapListWidgetItem * const item = new QtConceptMapListWidgetItem(s->GetCompetency());
      item->setText(s->GetText().c_str());
      item->setFlags(
            Qt::ItemIsSelectable
          | Qt::ItemIsEnabled
          | Qt::ItemIsEditable
          | Qt::ItemIsDragEnabled
          | Qt::ItemIsDropEnabled);
      ui->list_examples->addItem(item);
    }
  );

  assert(ui->list_examples->isEnabled());
  QObject::connect(
    ui->list_examples,
    SIGNAL(itemChanged(QListWidgetItem*)),
    this,
    SLOT(RemoveEmptyItem(QListWidgetItem*)));
}

ribi::cmap::QtConceptMapConceptEditDialog::~QtConceptMapConceptEditDialog() noexcept
{
  delete ui;
}

void ribi::cmap::QtConceptMapConceptEditDialog::keyPressEvent(QKeyEvent* e)
{
  if (e->key()  == Qt::Key_Escape) { close(); return; }

  if (e->key()  == Qt::Key_Enter || e->key()  == Qt::Key_Return)
  {
    if (e->modifiers() & Qt::AltModifier)
    {
      on_button_ok_clicked();
      return;
    }
  }
  //QDialog::keyPressEvent(e); //Causes dialog to close unwanted?
}

void ribi::cmap::QtConceptMapConceptEditDialog::on_button_add_clicked()
{
  //Close when the user has entered an empty edit
  if (ui->edit_text->text().isEmpty())
  {
    close();
    return;
  }

  {
    QListWidgetItem * const item = new QListWidgetItem;
    item->setText(ui->edit_text->text());
    item->setFlags(
          Qt::ItemIsSelectable
        | Qt::ItemIsEnabled
        | Qt::ItemIsEditable
        | Qt::ItemIsDragEnabled
        | Qt::ItemIsDropEnabled);
    ui->list_examples->addItem(item);
  }
  ui->edit_text->clear();
  ui->edit_text->setFocus();
}

void ribi::cmap::QtConceptMapConceptEditDialog::RemoveEmptyItem(QListWidgetItem * item)
{
  if (item->text().isEmpty())
  {
    delete item;
    this->update();
  }
}

#ifndef NDEBUG
void ribi::cmap::QtConceptMapConceptEditDialog::Test() noexcept
{
  {
    static bool is_tested = false;
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  {
    //Assume reading in a concept and clicking OK without modification does not modify anything
    const auto v = ribi::cmap::ConceptFactory().GetTests();
    std::for_each(v.begin(),v.end(),
      [](const boost::shared_ptr<ribi::cmap::Concept>& concept)
      {
        const boost::shared_ptr<const ribi::cmap::Concept> old_concept = ConceptFactory().DeepCopy(concept);
        assert(concept != old_concept);
        assert(*concept == *old_concept);
        QtConceptMapConceptEditDialog d(concept);
        //Do nothing...
        d.on_button_ok_clicked();
        #ifdef CONCEPTMAP_WRITE_TO_CONCEPT
        assert(d.WriteToConcept() == old_concept);
        #else
        assert(*concept == *old_concept);
        #endif
      }
    );
  }
  {
    //Assume reading in a concept and clicking OK after modification of the name does modify concept
    const auto v = ribi::cmap::ConceptFactory().GetTests();
    std::for_each(v.begin(),v.end(),
      [](const boost::shared_ptr<ribi::cmap::Concept>& concept)
      {
        const boost::shared_ptr<const ribi::cmap::Concept> old_concept = ConceptFactory().DeepCopy(concept);
        assert(*concept == *old_concept);
        QtConceptMapConceptEditDialog d(concept);
        d.ui->edit_concept->setText(d.ui->edit_concept->text() + "MODIFICATION");
        d.on_button_ok_clicked();
        #ifdef CONCEPTMAP_WRITE_TO_CONCEPT
        TRACE("TODO");
        #else
        //TODO
        //assert(concept != old_concept);
        #endif
      }
    );
  }
  {
    //Assume reading in a concept and clicking OK after adding an example
    const auto v = ribi::cmap::ConceptFactory().GetTests();
    std::for_each(v.begin(),v.end(),
      [](const boost::shared_ptr<ribi::cmap::Concept>& concept)
      {
        const boost::shared_ptr<const ribi::cmap::Concept> old_concept = ConceptFactory().DeepCopy(concept);
        assert(*concept == *old_concept);
        QtConceptMapConceptEditDialog d(concept);
        assert(d.ui->edit_text->text().isEmpty());
        d.ui->edit_text->setText("TO BE ADDED EXAMPLE");
        d.on_button_add_clicked(); //Should add
        d.on_button_ok_clicked();
        #ifdef CONCEPTMAP_WRITE_TO_CONCEPT
        TRACE("TODO");
        #else
        TRACE("TODO");
        //assert(concept != old_concept);
        #endif
      }
    );
  }
  {
    //Assume reading in a concept and NOT clicking OK does not change the concept,
    //even when having changed the name and examples in the GUI
    const auto v = ribi::cmap::ConceptFactory().GetTests();
    std::for_each(v.begin(),v.end(),
      [](const boost::shared_ptr<ribi::cmap::Concept>& concept)
      {
        const boost::shared_ptr<const ribi::cmap::Concept> old_concept = ConceptFactory().DeepCopy(concept);
        assert(*concept == *old_concept);
        QtConceptMapConceptEditDialog d(concept);
        //Change name
        d.ui->edit_concept->setText(d.ui->edit_concept->text() + "MODIFICATION");
        //Change examples
        assert(d.ui->edit_text->text().isEmpty());
        d.ui->edit_text->setText("TO BE ADDED EXAMPLE");
        d.on_button_add_clicked(); //Should add
        //DO NOT PRESS OK d.on_button_ok_clicked();
        #ifdef CONCEPTMAP_WRITE_TO_CONCEPT
        TRACE("TODO");
        #else
        assert(*concept == *old_concept);
        #endif
      }
    );
  }
}
#endif

void ribi::cmap::QtConceptMapConceptEditDialog::on_button_ok_clicked()
{
  #ifndef CONCEPTMAP_WRITE_TO_CONCEPT
  assert(m_concept);
  //Name
  const std::string name = ui->edit_concept->text().toStdString();
  //Examples
  std::vector<boost::shared_ptr<cmap::Example> > v;

  const int n_items = ui->list_examples->count();
  for (int i=0; i!=n_items; ++i)
  {
    const QListWidgetItem * const item = ui->list_examples->item(i);
    const QtConceptMapListWidgetItem * const pvdb_item = dynamic_cast<const QtConceptMapListWidgetItem *>(item);
    const cmap::Competency competency = pvdb_item ? pvdb_item->m_competency : cmap::Competency::uninitialized;
    boost::shared_ptr<cmap::Example> p(
      ExampleFactory().Create(
        item->text().toStdString(),
        competency
      )
    );
    v.push_back(p);
  }
  assert(n_items == boost::numeric_cast<int>(v.size()));
  //Set to concept
  const boost::shared_ptr<ribi::cmap::Examples> examples(new cmap::Examples(v));
  assert(examples);
  assert(this);
  assert(m_concept);
  assert(m_concept->GetExamples());
  m_concept->SetName(name);
  m_concept->SetExamples(examples);
  #endif
  close();
}

#ifdef CONCEPTMAP_WRITE_TO_CONCEPT
const boost::shared_ptr<ribi::cmap::Concept> ribi::cmap::QtConceptMapConceptEditDialog::WriteToConcept() const
{
  //Name
  const std::string name = ui->edit_concept->text().toStdString();
  //Examples
  std::vector<boost::shared_ptr<cmap::Example> > v;

  const int n_items = ui->list_examples->count();
  for (int i=0; i!=n_items; ++i)
  {
    const QListWidgetItem * const item = ui->list_examples->item(i);
    const QtConceptMapListWidgetItem * const pvdb_item = dynamic_cast<const QtConceptMapListWidgetItem *>(item);
    const cmap::Competency competency = pvdb_item ? pvdb_item->m_competency : cmap::Competency::uninitialized;
    boost::shared_ptr<cmap::Example> p(
      cmap::ExampleFactory().Create(
        item->text().toStdString(),
        competency
      )
    );
    v.push_back(p);
  }
  assert(n_items == boost::numeric_cast<int>(v.size()));
  //Set to concept
  const boost::shared_ptr<ribi::cmap::Examples> examples(new cmap::Examples(v));
  assert(examples);
  const boost::shared_ptr<Concept> concept
    = ribi::cmap::ConceptFactory().Create(
      name,
      examples,
      m_rating_complexity,
      m_rating_concreteness,
      m_rating_specificity);

  assert(concept);
  return concept;
}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmapdisplaystrategy.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2014 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPDISPLAYSTRATEGY_H
#define QTCONCEPTMAPDISPLAYSTRATEGY_H

#ifdef USE_ITEMDISPLAYSTRATEGY_20140622

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qtitemdisplaystrategy.h"
#include "conceptmapfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

///QtDisplayStrategy display a Concept
struct QtDisplayStrategy : public QtItemDisplayStrategy
{
  ~QtDisplayStrategy() noexcept;

  //concept can be modified (as it is not const), but I can promise I will try to prevent this from happening
  explicit QtDisplayStrategy(const boost::shared_ptr<Concept>& concept);

protected:
  virtual void keyPressEvent(QKeyEvent *) noexcept {} //Do not respond to key presses

private:
  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

  void UpdateBrushesAndPens() noexcept;
};

} //~namespace cmap
} //~namespace ribi

#endif // USE_ITEMDISPLAYSTRATEGY_20140622


#endif // QTCONCEPTMAPDISPLAYSTRATEGY_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapdisplaystrategy.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2014 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifdef USE_ITEMDISPLAYSTRATEGY_20140622

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapdisplaystrategy.h"

#include <cstdlib>
#include <sstream>

#include <QKeyEvent>

#include "conceptmapexample.h"
#include "conceptmapexamples.h"
#include "conceptmaphelper.h"
#include "conceptmapconcept.h"
#include "conceptmapcompetency.h"
#include "conceptmapconceptfactory.h"
#include "qtconceptmapbrushfactory.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::cmap::QtDisplayStrategy::QtDisplayStrategy(const boost::shared_ptr<ribi::cmap::Concept>& concept)
  : QtItemDisplayStrategy(concept)
{
  #ifndef NDEBUG
  Test();
  assert(concept);
  assert(GetConcept());
  #endif

  UpdateBrushesAndPens(); //NEVER CALL VIRTUAL FUNCTIONS IN CONSTRUCTORS

  //?FIX 2013-01-06 22:47
  GetConcept()->m_signal_name_changed.connect(
    boost::bind(&ribi::cmap::QtDisplayStrategy::OnConceptNameChanged,this)); //Obligatory

  GetConcept()->m_signal_examples_changed.connect( //FIX 2013-01-06 22:32
      boost::bind(
        &ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens,
        this
      )
    );

  GetConcept()->m_signal_rating_complexity_changed.connect(
      boost::bind(
        &ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens,
        this
      )
    );
  GetConcept()->m_signal_rating_concreteness_changed.connect(
      boost::bind(
        &ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens,
        this
      )
    );
  GetConcept()->m_signal_rating_specificity_changed.connect(
      boost::bind(
        &ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens,
        this
      )
    );
}

ribi::cmap::QtDisplayStrategy::~QtDisplayStrategy() noexcept
{
  GetConcept()->m_signal_examples_changed.disconnect(
      boost::bind(
        &ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens,
        this
      )
    );

  //Obligatory: because concepts live longer than DisplayConceptItems,
  //these Concepts will signal Items when the Item is destroyed,
  //which results in a segmentation fault
  GetConcept()->m_signal_rating_complexity_changed.disconnect(
      boost::bind(
        &ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens,
        this
      )
    );
  GetConcept()->m_signal_rating_concreteness_changed.disconnect(
      boost::bind(
        &ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens,
        this
      )
    );
  GetConcept()->m_signal_rating_specificity_changed.disconnect(
      boost::bind(
        &ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens,
        this
      )
    );
}

#ifndef NDEBUG
void ribi::cmap::QtDisplayStrategy::Test() noexcept
{
  {
    static bool is_tested = false;
    if (is_tested) return;
    is_tested = true;
  }
  TRACE("Starting ribi::cmap::QtDisplayStrategy::Test()");
  TRACE("Successfully finished ribi::cmap::QtDisplayStrategy::Test()");
}
#endif


void ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens() noexcept
{
  //TRACE("Start of void ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens()");
  assert(GetConcept());
  assert(GetConcept()->GetExamples());

  //Brush for the concept being rated
  QBrush new_main_brush = this->brush();
  {
    const int n_rated
      = (GetConcept()->GetRatingComplexity()   != -1 ? 1 : 0)
      + (GetConcept()->GetRatingConcreteness() != -1 ? 1 : 0)
      + (GetConcept()->GetRatingSpecificity()  != -1 ? 1 : 0);
    switch (n_rated)
    {
      case 0:
        new_main_brush = QtBrushFactory::CreateRedGradientBrush();
        break;
      case 1:
      case 2:
        new_main_brush = QtBrushFactory::CreateYellowGradientBrush();
        break;
      case 3:
        new_main_brush = QtBrushFactory::CreateGreenGradientBrush();
        break;
      default: assert(!"Should not get here");
    }
  }
  //Brush and pen for the examples being rated
  QBrush new_indicator_brush = this->GetIndicatorBrush();
  QPen new_indicator_pen = this->GetIndicatorPen();
  if (GetConcept()->GetExamples()->Get().empty())
  {
    //No examples
    new_indicator_brush = QBrush(QColor(0,0,0));
    new_indicator_pen = QPen(QColor(0,0,0));
  }
  else
  {
    const std::vector<boost::shared_ptr<const cmap::Example> > v = AddConst(GetConcept()->GetExamples()->Get());
    const int n_examples = boost::numeric_cast<int>(v.size());
    const int n_judged
      = std::count_if(v.begin(),v.end(),
        [](const boost::shared_ptr<const cmap::Example>& p)
        {
          assert(p);
          const cmap::Competency this_competency = p->GetCompetency();
          return this_competency != cmap::Competency::uninitialized;
        }
      );
    if (n_judged == 0)
    {
      new_indicator_brush = QBrush(QColor(255,128,128)); //Red
    }
    else if (n_judged < n_examples)
    {
      new_indicator_brush = QBrush(QColor(255,196,128)); //Orange
    }
    else
    {
      assert(n_judged == n_examples);
      new_indicator_brush = QBrush(QColor(128,255,128)); //Green
    }
    if (n_judged == 0)
    {
      new_indicator_pen = QPen(QColor(255,0,0),3); //Thick pen
    }
    else if (n_judged < n_examples)
    {
      new_indicator_pen = QPen(QColor(255,196,0),2); //Less thick pen
    }
    else
    {
      assert(n_judged == n_examples);
      new_indicator_pen = QPen(QColor(0,255,0),1); //Thin pen
    }
  }
  if (this->brush() != new_main_brush
    || this->GetIndicatorBrush() != new_indicator_brush
    || this->GetIndicatorPen() != new_indicator_pen)
  {
    this->setBrush(new_main_brush);
    this->SetIndicatorBrush(new_indicator_brush);
    this->SetIndicatorPen(new_indicator_pen);
    assert(this->brush() == new_main_brush);
    assert(this->GetIndicatorBrush() == new_indicator_brush);
    assert(this->GetIndicatorPen() == new_indicator_pen);
    //TRACE(std::rand()); //GOOD: Detects infinite recursion
    //this->update();
    this->m_signal_item_has_updated(this); //Obligatory
    this->m_signal_request_scene_update(); //Obligatory
  }
  //TRACE("End of void ribi::cmap::QtDisplayStrategy::UpdateBrushesAndPens()");
}

#endif // USE_ITEMDISPLAYSTRATEGY_20140622

 

 

 

 

 

./CppQtConceptMap/qtconceptmapedgedialog.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPEDGEDIALOG_H
#define QTCONCEPTMAPEDGEDIALOG_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace Ui { class QtEdgeDialog; }

namespace ribi {
namespace cmap {

///Displays and modifies an Edge
class QtEdgeDialog : public ribi::QtHideAndShowDialog
{
  Q_OBJECT

public:
  explicit QtEdgeDialog(QWidget *parent = 0);
  QtEdgeDialog(const QtEdgeDialog&) = delete;
  QtEdgeDialog& operator=(const QtEdgeDialog&) = delete;
  ~QtEdgeDialog();

  boost::shared_ptr<Edge> GetEdge() const noexcept { return m_edge; }
  static int GetMinimumHeight(const Edge& edge) noexcept;

  ///Get if there is an arrow at the head of the edge directly from the GUI
  bool GetUiHasHeadArrow() const noexcept;
  ///Get if there is an arrow at the tail of the edge directly from the GUI
  bool GetUiHasTailArrow() const noexcept;
  ///Get the X coordinat directly from the GUI
  double GetUiX() const noexcept;
  ///Get the Y coordinat directly from the GUI
  double GetUiY() const noexcept;

  void SetEdge(const boost::shared_ptr<Edge>& edge);

  ///Set if there is an arrow at the head of the edge directly to the GUI
  void SetUiHasHeadArrow(const bool has_head) noexcept;
  ///Set if there is an arrow at the tail of the edge directly to the GUI
  void SetUiHasTailArrow(const bool has_tail) noexcept;
  ///Set the X coordinat directly to the GUI
  void SetUiX(const double x ) noexcept;
  ///Set the Y coordinat directly to the GUI
  void SetUiY(const double y) noexcept;

private slots:

  void on_box_head_arrow_stateChanged(int arg1);
  void on_box_tail_arrow_stateChanged(int arg1);

private:
  Ui::QtEdgeDialog *ui;

  ///The Edge to work on
  boost::shared_ptr<Edge> m_edge;

  boost::shared_ptr<QtNodeDialog> m_qtnodedialog; //The center node
  boost::shared_ptr<QtNodeDialog> m_qtnodedialog_from;
  boost::shared_ptr<QtNodeDialog> m_qtnodedialog_to;

  //edge is non-const, as its displayal by this dialog renders it editable
  void OnFromChanged(Edge * const edge);
  void OnHeadArrowChanged(Edge * const edge);
  void OnNodeChanged(Edge * const edge);
  void OnTailArrowChanged(Edge * const edge);
  void OnToChanged(Edge * const edge);

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPEDGEDIALOG_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapedgedialog.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapedgedialog.h"

#include <cassert>
#include <boost/bind/bind.hpp>
#include <boost/lambda/lambda.hpp>

#include <QLabel>

#include "conceptmapedge.h"
#include "conceptmapedgefactory.h"
#include "conceptmapnode.h"
#include "conceptmapnodefactory.h"
#include "conceptmapconcept.h"
#include "conceptmapconceptfactory.h"
#include "qtconceptmapexampledialog.h"
#include "qtconceptmapexamplesdialog.h"
#include "qtconceptmapconceptdialog.h"
#include "qtconceptmapnodedialog.h"
#include "ui_qtconceptmapedgedialog.h"
#include "testtimer.h"
#include "trace.h"

#pragma GCC diagnostic pop

ribi::cmap::QtEdgeDialog::QtEdgeDialog(QWidget *parent)
  : ribi::QtHideAndShowDialog(parent),
    ui(new Ui::QtEdgeDialog),
    m_edge{},
    m_qtnodedialog{new QtNodeDialog},
    m_qtnodedialog_from{new QtNodeDialog},
    m_qtnodedialog_to{new QtNodeDialog}
{
  ui->setupUi(this);
  #ifndef NDEBUG
  Test();
  #endif
  {
    assert(ui->groupBox->layout());
    const auto my_layout = ui->groupBox->layout();
    {
      QLabel * const label = new QLabel("Center node:");
      my_layout->addWidget(label);
    }
    my_layout->addWidget(m_qtnodedialog.get());
    {
      QLabel * const label = new QLabel("From node:");
      my_layout->addWidget(label);
    }
    my_layout->addWidget(m_qtnodedialog_from.get());
    {
      QLabel * const label = new QLabel("To node:");
      my_layout->addWidget(label);
    }
    my_layout->addWidget(m_qtnodedialog_to.get());
  }
}

ribi::cmap::QtEdgeDialog::~QtEdgeDialog()
{
  delete ui;
}

int ribi::cmap::QtEdgeDialog::GetMinimumHeight(const Edge& edge) noexcept
{
  return
      QtNodeDialog::GetMinimumHeight(*edge.GetFrom())
    + QtNodeDialog::GetMinimumHeight(*edge.GetNode())
    + QtNodeDialog::GetMinimumHeight(*edge.GetTo())
    + 200
  ;
}


bool ribi::cmap::QtEdgeDialog::GetUiHasHeadArrow() const noexcept
{
  return ui->box_head_arrow->isChecked();
}

bool ribi::cmap::QtEdgeDialog::GetUiHasTailArrow() const noexcept
{
  return ui->box_tail_arrow->isChecked();
}

double ribi::cmap::QtEdgeDialog::GetUiX() const noexcept
{
  return m_qtnodedialog->GetUiX();
}

double ribi::cmap::QtEdgeDialog::GetUiY() const noexcept
{
  return m_qtnodedialog->GetUiY();
}

void ribi::cmap::QtEdgeDialog::SetEdge(const boost::shared_ptr<Edge>& edge)
{
  const bool verbose{false};

  assert(edge);
  if (m_edge == edge)
  {
    return;
  }

  if (verbose)
  {
    std::stringstream s;
    s << "Setting edge '" << edge->ToStr() << "'\n";
  }
  const auto from_after = edge->GetFrom();
  const auto head_arrow_after = edge->HasHeadArrow();
  const auto node_after = edge->GetNode();
  const auto tail_arrow_after = edge->HasTailArrow();
  const auto to_after = edge->GetTo();

  bool from_changed  = true;
  bool head_arrow_changed  = true;
  bool node_changed  = true;
  bool tail_arrow_changed  = true;
  bool to_changed  = true;

  if (m_edge)
  {
    const auto from_before = m_edge->GetFrom();
    const auto head_arrow_before = m_edge->HasHeadArrow();
    const auto node_before = m_edge->GetNode();
    const auto tail_arrow_before = m_edge->HasTailArrow();
    const auto to_before = m_edge->GetTo();


    from_changed = from_before != from_after;
    head_arrow_changed = head_arrow_before != head_arrow_after;
    node_changed = node_before != node_after;
    tail_arrow_changed = tail_arrow_before != tail_arrow_after;
    to_changed = to_before != to_after;


    if (verbose)
    {
      if (from_changed)
      {
        std::stringstream s;
        s
          << "From will change from "
          << from_before->ToStr()
          << " to "
          << from_after->ToStr()
          << '\n'
        ;
        TRACE(s.str());
      }
      if (head_arrow_changed)
      {
        std::stringstream s;
        s
          << "Head arrow will change from "
          << head_arrow_before
          << " to "
          << head_arrow_after
          << '\n'
        ;
        TRACE(s.str());
      }
      if (node_changed)
      {
        std::stringstream s;
        s
          << "Node will change from "
          << node_before->ToStr()
          << " to "
          << node_after->ToStr()
          << '\n'
        ;
        TRACE(s.str());
      }
      if (tail_arrow_changed)
      {
        std::stringstream s;
        s
          << "Tail arrow will change from "
          << tail_arrow_before
          << " to "
          << tail_arrow_after
          << '\n'
        ;
        TRACE(s.str());
      }
      if (to_changed)
      {
        std::stringstream s;
        s
          << "To will change from "
          << to_before->ToStr()
          << " to "
          << to_after->ToStr()
          << '\n'
        ;
        TRACE(s.str());
      }
    }
    //Disconnect
    m_edge->m_signal_from_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdgeDialog::OnFromChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_head_arrow_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdgeDialog::OnHeadArrowChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_node_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdgeDialog::OnNodeChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_tail_arrow_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdgeDialog::OnTailArrowChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_to_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdgeDialog::OnToChanged,this,boost::lambda::_1)
    );
  }

  //Replace m_example by the new one
  m_edge = edge;

  assert(m_edge->GetFrom() == from_after );
  assert(m_edge->HasHeadArrow() == head_arrow_after );
  assert(m_edge->GetNode() == node_after );
  assert(m_edge->HasTailArrow() == tail_arrow_after );
  assert(m_edge->GetTo() == to_after );

  //Connect
  m_edge->m_signal_from_changed.connect(
    boost::bind(&ribi::cmap::QtEdgeDialog::OnFromChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_head_arrow_changed.connect(
    boost::bind(&ribi::cmap::QtEdgeDialog::OnHeadArrowChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_node_changed.connect(
    boost::bind(&ribi::cmap::QtEdgeDialog::OnNodeChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_tail_arrow_changed.connect(
    boost::bind(&ribi::cmap::QtEdgeDialog::OnTailArrowChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_to_changed.connect(
    boost::bind(&ribi::cmap::QtEdgeDialog::OnToChanged,this,boost::lambda::_1)
  );

  //Emit everything that has changed
  if (from_changed)
  {
    m_edge->m_signal_from_changed(m_edge.get());
  }
  if (head_arrow_changed)
  {
    m_edge->m_signal_head_arrow_changed(m_edge.get());
  }
  if (node_changed)
  {
    m_edge->m_signal_node_changed(m_edge.get());
  }
  if (tail_arrow_changed)
  {
    m_edge->m_signal_tail_arrow_changed(m_edge.get());
  }
  if (to_changed)
  {
    m_edge->m_signal_to_changed(m_edge.get());
  }

  setMinimumHeight(GetMinimumHeight(*m_edge));

  assert( edge ==  m_edge);
  assert(*edge == *m_edge);
}

void ribi::cmap::QtEdgeDialog::OnFromChanged(Edge * const edge)
{
  const bool verbose{false};
  assert(edge);

  const auto from_before = m_qtnodedialog_from->GetNode();
  const auto from_after = edge->GetFrom();

  if (verbose)
  {
    std::stringstream s;
    s << "Change from from "
    << (from_before ? from_before->ToStr() : "[NONE]")
    << " to " << from_after->ToStr();
    TRACE(s.str());
  }

  m_qtnodedialog_from->SetNode(from_after);

  assert(m_qtnodedialog_from->GetNode() == from_after);
}

void ribi::cmap::QtEdgeDialog::OnHeadArrowChanged(Edge * const edge)
{
  const bool verbose{false};
  assert(edge);

  const auto head_arrow_before = ui->box_head_arrow->isChecked();
  const auto head_arrow_after = edge->HasHeadArrow();

  if (verbose)
  {
    std::stringstream s;
    s << "Change head arrow from "
      << head_arrow_before
      << " to " << head_arrow_after
    ;
    TRACE(s.str());
  }

  ui->box_head_arrow->setChecked(head_arrow_after);

  assert(ui->box_head_arrow->isChecked() == head_arrow_after);
}

void ribi::cmap::QtEdgeDialog::OnNodeChanged(Edge * const edge)
{
  const bool verbose{false};
  assert(edge);

  const auto node_before = m_qtnodedialog->GetNode();
  const auto node_after = edge->GetNode();

  if (verbose)
  {
    std::stringstream s;
    s << "Change center node from "
    << (node_before ? node_before->ToStr() : "[NONE]")
    << " to " << node_after->ToStr();
    TRACE(s.str());
  }

  m_qtnodedialog->SetNode(node_after);

  assert(m_qtnodedialog->GetNode() == node_after);
}


void ribi::cmap::QtEdgeDialog::OnTailArrowChanged(Edge * const edge)
{
  const bool verbose{false};
  assert(edge);

  const auto tail_arrow_before = ui->box_tail_arrow->isChecked();
  const auto tail_arrow_after = edge->HasTailArrow();

  if (verbose)
  {
    std::stringstream s;
    s << "Change tail arrow from "
      << tail_arrow_before
      << " to " << tail_arrow_after
    ;
    TRACE(s.str());
  }

  ui->box_tail_arrow->setChecked(tail_arrow_after);

  assert(ui->box_tail_arrow->isChecked() == tail_arrow_after);
}


void ribi::cmap::QtEdgeDialog::OnToChanged(Edge * const edge)
{
  const bool verbose{false};
  assert(edge);

  const auto to_before = m_qtnodedialog_to->GetNode();
  const auto to_after = edge->GetTo();

  if (verbose)
  {
    std::stringstream s;
    s << "Change to from "
    << (to_before ? to_before->ToStr() : "[NONE]")
    << " to " << to_after->ToStr();
    TRACE(s.str());
  }

  m_qtnodedialog_to->SetNode(to_after);

  assert(m_qtnodedialog_to->GetNode() == to_after);
}

void ribi::cmap::QtEdgeDialog::SetUiHasHeadArrow(const bool has_head) noexcept
{
  ui->box_head_arrow->setChecked(has_head);
}

void ribi::cmap::QtEdgeDialog::SetUiHasTailArrow(const bool has_tail) noexcept
{
  ui->box_tail_arrow->setChecked(has_tail);
}

void ribi::cmap::QtEdgeDialog::SetUiX(const double x) noexcept
{
  m_qtnodedialog->SetUiX(x);
}


void ribi::cmap::QtEdgeDialog::SetUiY(const double y) noexcept
{
  m_qtnodedialog->SetUiY(y);
}


#ifndef NDEBUG
void ribi::cmap::QtEdgeDialog::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  NodeFactory();
  EdgeFactory();
  QtExampleDialog();
  QtExamplesDialog();
  QtNodeDialog();

  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  const auto from = NodeFactory().GetTest(0);
  const auto to = NodeFactory().GetTest(0);
  const boost::shared_ptr<Edge> edge = EdgeFactory().GetTest(0,from,to);
  QtEdgeDialog dialog;
  dialog.SetEdge(edge);
  if (verbose) { TRACE("X of QtNode and QtNodeDialog must match at start"); }
  {
    assert(std::abs(dialog.GetUiX() - edge->GetNode()->GetX()) < 2.0);
  }
  if (verbose) { TRACE("SetX and GetX must be symmetric"); }
  {
    const double new_x{dialog.GetUiX() + 10.0};
    dialog.SetUiX(new_x);
    assert(std::abs(dialog.GetUiX() - new_x) < 1.0);
  }
  if (verbose) { TRACE("SetY and GetY must be symmetric"); }
  {
    const double new_y{dialog.GetUiY() + 10.0};
    dialog.SetUiY(new_y);
    assert(std::abs(dialog.GetUiY() - new_y) < 1.0);
  }
  if (verbose) { TRACE("If X is set via Edge, QtNodeDialog must sync"); }
  {
    const double old_x{edge->GetNode()->GetX()};
    const double new_x{old_x + 10.0};
    edge->GetNode()->SetX(new_x);
    assert(std::abs(new_x - dialog.GetUiX()) < 2.0);
  }
  if (verbose) { TRACE("If X is set via QtNodeDialog, Edge must sync"); }
  {
    const double old_x{dialog.GetUiX()};
    const double new_x{old_x + 10.0};
    dialog.SetUiX(new_x);
    assert(std::abs(new_x - edge->GetNode()->GetX()) < 2.0);
  }

  if (verbose) { TRACE("SetUiHasHeadArrow and GetUiHasHeadArrow must be symmetric"); }
  {
    dialog.SetUiHasHeadArrow(true);
    assert(dialog.GetUiHasHeadArrow());
    dialog.SetUiHasHeadArrow(false);
    assert(!dialog.GetUiHasHeadArrow());
  }
  if (verbose) { TRACE("SetUiHasTailArrow and GetUiHasTailArrow must be symmetric"); }
  {
    dialog.SetUiHasHeadArrow(true);
    assert(dialog.GetUiHasHeadArrow());
    dialog.SetUiHasHeadArrow(false);
    assert(!dialog.GetUiHasHeadArrow());
  }
}
#endif

void ribi::cmap::QtEdgeDialog::on_box_head_arrow_stateChanged(int)
{
  m_edge->SetHeadArrow(ui->box_head_arrow->isChecked());
}

void ribi::cmap::QtEdgeDialog::on_box_tail_arrow_stateChanged(int)
{
  m_edge->SetTailArrow(ui->box_tail_arrow->isChecked());
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapedgefactory.h

 

 

 

 

 

 

./CppQtConceptMap/qtconceptmapeditstrategy.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2014 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPEDITSTRATEGY_H
#define QTCONCEPTMAPEDITSTRATEGY_H

#ifdef USE_ITEMDISPLAYSTRATEGY_20140622

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qtitemdisplaystrategy.h"
#include "conceptmapfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

///Displays a Concept that might be edited
///QtEditStrategy -> QtEditStrategy
struct QtEditStrategy : public QtItemDisplayStrategy
{
  //concept will be modified
  explicit QtEditStrategy(const boost::shared_ptr<Concept> concept);
  ~QtEditStrategy() noexcept;
  ///Signalled when the user wants to edit
  boost::signals2::signal<void (QtItemDisplayStrategy *)> m_signal_request_edit;

protected:
  void keyPressEvent(QKeyEvent *event) noexcept;

private:
  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

  ///A Edit ConceptItem does not change its brushes and pens
  void UpdateBrushesAndPens() noexcept {}
};

} //~namespace cmap
} //~namespace ribi

#endif // USE_ITEMDISPLAYSTRATEGY_20140622

#endif // QTCONCEPTMAPEDITSTRATEGY_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapeditstrategy.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2014 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifdef USE_ITEMDISPLAYSTRATEGY_20140622

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapeditstrategy.h"

#include <QKeyEvent>

#include "conceptmapconcept.h"
#include "conceptmapconceptfactory.h"
#include "qtconceptmapbrushfactory.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::cmap::QtEditStrategy::QtEditStrategy(const boost::shared_ptr<Concept> concept)
  : QtItemDisplayStrategy(concept),
    m_signal_request_edit{}
{
  #ifndef NDEBUG
  Test();
  assert(concept);
  assert(GetConcept());
  #endif

  this->setBrush(QtBrushFactory::CreateGrayGradientBrush()); //NEW 2013-04-09

  GetConcept()->m_signal_name_changed.connect(
    boost::bind(&ribi::cmap::QtEditStrategy::OnConceptNameChanged,this)); //Obligatory

  //GetConcept()->m_signal_rating_complexity_changed.connect(
  //  boost::bind(&ribi::cmap::QtEditStrategy::UpdateBrushesAndPens,this));
  //GetConcept()->m_signal_rating_concreteness_changed.connect(
  //  boost::bind(&ribi::cmap::QtEditStrategy::UpdateBrushesAndPens,this));
  //GetConcept()->m_signal_rating_specificity_changed.connect(
  //  boost::bind(&ribi::cmap::QtEditStrategy::UpdateBrushesAndPens,this));
}

ribi::cmap::QtEditStrategy::~QtEditStrategy() noexcept
{
  GetConcept()->m_signal_name_changed.disconnect(
    boost::bind(&ribi::cmap::QtEditStrategy::OnConceptNameChanged,this));
}

void ribi::cmap::QtEditStrategy::keyPressEvent(QKeyEvent *event) noexcept
{
  switch (event->key())
  {
    case Qt::Key_F2:
      m_signal_request_edit(this);
      m_signal_request_scene_update(); //???really needed
    return; //Done
    break;
  }
}

#ifndef NDEBUG
void ribi::cmap::QtEditStrategy::Test() noexcept
{
  {
    static bool is_tested = false;
    if (is_tested) return;
    is_tested = true;
  }
  TRACE("Starting ribi::cmap::QtEditStrategy::Test()");
  ///Test SetText
  {
    const boost::shared_ptr<Concept> concept = ConceptFactory().Create();
    QtEditStrategy a(concept);
    const auto v {
      "1234567890",
      "1234567890 1234567890",
      "1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890",
      "1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890"
    };
    for (const auto s: v) { a.SetName(s); } //SetName tests GetName
  }
  TRACE("Successfully finished ribi::cmap::QtEditStrategy::Test()");
}
#endif

#endif // USE_ITEMDISPLAYSTRATEGY_20140622

 

 

 

 

 

./CppQtConceptMap/qtconceptmapelement.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPCONCEPTMAPITEM_H
#define QTCONCEPTMAPCONCEPTMAPITEM_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/signals2.hpp>
#include "qtconceptmapfwd.h"
#include "qtroundededitrectitem.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

///A focusable element of a concept map, which is either a Node or an Edge
///QtRoundedTextRectItem: single line
///QtRoundedEditRectItem: multiple lines
struct QtConceptMapElement : public QtRoundedEditRectItem
{
  QtConceptMapElement();
  virtual ~QtConceptMapElement() noexcept {}

  //virtual void DisableAll() = 0;
  //virtual void EnableAll() = 0;

  ///Obtain the Concept from either a Node or an Edge
  //virtual boost::shared_ptr<const Node>  GetNode() const noexcept = 0;
  //virtual boost::shared_ptr<      Node>  GetNode()       noexcept = 0;

  //virtual boost::shared_ptr<const QtItemDisplayStrategy> GetDisplayStrategy() const noexcept = 0;
  //virtual boost::shared_ptr<      QtItemDisplayStrategy> GetDisplayStrategy()       noexcept = 0;

  ///Set the name of the concept
  //virtual void SetName(const std::string& name) noexcept = 0;

  ///Set the position
  //void SetPos(const double x, const double y) noexcept { SetX(x); SetY(y); }

  ///Set the X coordinat
  //virtual void SetX(const double x) noexcept = 0;

  ///Set the Y coordinat
  //virtual void SetY(const double y) noexcept = 0;

  ///A more specific signal: a Concept requests an edit, this is passed to
  ///OnConceptRequestsEdit, which lets this QtConceptMapItem request for an edit
  //boost::signals2::signal<void(QtConceptMapElement*)> m_signal_conceptmapitem_requests_edit;

  ///Slot for a Concept its signal to be edited, all it does is add the ConceptMapItem
  ///the Concept is a member of
  //void OnConceptRequestsEdit();

  protected:
  ///Remove this member function, let the client call the virual SetX and SetY member functions
  //void setPos(const QPointF &pos) { QtRoundedEditRectItem::setPos(pos); }
  ///Remove this member function, let the client call the virual SetX and SetY member functions
  //void setPos(qreal x, qreal y) { QtRoundedEditRectItem::setPos(x,y); }

  void hoverMoveEvent(QGraphicsSceneHoverEvent *) noexcept override final;
  void hoverEnterEvent(QGraphicsSceneHoverEvent *) noexcept override final;


  private:
  //virtual void SetNode(const boost::shared_ptr<Node>& node) = 0;

};

} //~namespace cmap

} //~namespace ribi

#endif // QTCONCEPTMAPCONCEPTMAPITEM_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapelement.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapelement.h"

#include <QCursor>

#include "conceptmapconcept.h"
#include "conceptmapnode.h"
#include "conceptmapexamples.h"
#include "qtitemdisplaystrategy.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::cmap::QtConceptMapElement::QtConceptMapElement()
  : QtRoundedEditRectItem()
{

}

/*
void ribi::cmap::QtConceptMapElement::OnConceptRequestsEdit()
{
  assert(this);
  assert(this->GetNode());
  assert(this->GetNode()->GetConcept()->GetExamples());
  this->setAcceptHoverEvents(true);
  assert(!"TODO");
  //m_signal_conceptmapitem_requests_edit(this);
}
*/

void ribi::cmap::QtConceptMapElement::hoverMoveEvent(QGraphicsSceneHoverEvent *) noexcept
{
  this->setCursor(QCursor(Qt::PointingHandCursor));
}

void ribi::cmap::QtConceptMapElement::hoverEnterEvent(QGraphicsSceneHoverEvent *) noexcept
{
  this->setCursor(QCursor(Qt::PointingHandCursor));
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapexampledialog.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPEXAMPLEDIALOG_H
#define QTCONCEPTMAPEXAMPLEDIALOG_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace Ui { class QtExampleDialog; }

namespace ribi {
namespace cmap {

///Displays and modifies an Example
class QtExampleDialog : public ribi::QtHideAndShowDialog
{
  Q_OBJECT

public:
  explicit QtExampleDialog(QWidget *parent = 0);
  QtExampleDialog(const QtExampleDialog&) = delete;
  QtExampleDialog& operator=(const QtExampleDialog&) = delete;
  ~QtExampleDialog();

  boost::shared_ptr<Example> GetExample() const noexcept { return m_example; }
  static int GetMinimumHeight(const Example& example) noexcept;
  void SetExample(const boost::shared_ptr<Example>& example);

private slots:
  void on_box_competency_currentIndexChanged(int index);
  void on_box_is_complex_stateChanged(int arg1);
  void on_box_is_concrete_stateChanged(int arg1);
  void on_box_is_specific_stateChanged(int arg1);
  void on_edit_text_textChanged(const QString &arg1);

private:
  Ui::QtExampleDialog *ui;

  ///The Example to work on
  boost::shared_ptr<Example> m_example;

  void OnCompetencyChanged(const Example * const example);
  void OnIsComplexChanged(const Example * const example);
  void OnIsConcreteChanged(const Example * const example);
  void OnIsSpecificChanged(const Example * const example);
  void OnTextChanged(const Example * const example);

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPEXAMPLEDIALOG_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapexampledialog.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapexampledialog.h"
#include "ui_qtconceptmapexampledialog.h"

#include <cassert>
#include <boost/bind/bind.hpp>
#include <boost/lambda/lambda.hpp>

#include "conceptmapcompetencies.h"
#include "conceptmapexample.h"
#include "conceptmapexamplefactory.h"
#include "testtimer.h"
#include "trace.h"

#pragma GCC diagnostic pop

ribi::cmap::QtExampleDialog::QtExampleDialog(QWidget *parent) :
  ribi::QtHideAndShowDialog(parent),
  ui(new Ui::QtExampleDialog),
  m_example{}
{
  ui->setupUi(this);
  #ifndef NDEBUG
  Test();
  #endif
  {
    const auto v = Competencies().GetAllCompetencies();
    for (const auto c: v)
    {
      const std::string s = Competencies().ToStrDutch(c);
      ui->box_competency->addItem(s.c_str());
    }
  }

  const auto example
    = ExampleFactory().Create("QtExampleDialog initial example",Competency::uninitialized,false,false,false);
  this->SetExample(example);
}

ribi::cmap::QtExampleDialog::~QtExampleDialog()
{
  delete ui;
}

int ribi::cmap::QtExampleDialog::GetMinimumHeight(const Example& /*example*/) noexcept
{
  return 166;
}

void ribi::cmap::QtExampleDialog::SetExample(const boost::shared_ptr<Example>& example)
{
  const bool verbose{false};

  assert(example);
  if (m_example == example)
  {
    return;
  }
  if (verbose)
  {
    std::stringstream s;
    s << "Setting example '" << example->ToStr() << "'\n";
  }

  const auto competency_after  = example->GetCompetency();
  const auto is_complex_after  = example->GetIsComplex();
  const auto is_concrete_after = example->GetIsConcrete();
  const auto is_specific_after = example->GetIsSpecific();
  const auto text_after        = example->GetText();


  bool competency_changed  = true;
  bool is_complex_changed  = true;
  bool is_concrete_changed = true;
  bool is_specific_changed = true;
  bool text_changed = true;

  if (m_example)
  {
    const auto competency_before  = m_example->GetCompetency();
    const auto is_complex_before  = m_example->GetIsComplex();
    const auto is_concrete_before = m_example->GetIsConcrete();
    const auto is_specific_before = m_example->GetIsSpecific();
    const auto text_before        = m_example->GetText();

    competency_changed  = competency_before != competency_after;
    is_complex_changed  = is_complex_before != is_complex_after;
    is_concrete_changed = is_concrete_before != is_concrete_after;
    is_specific_changed = is_specific_before != is_specific_after;
    text_changed = text_before != text_after;


    if (verbose)
    {
      if (competency_changed)
      {
        std::stringstream s;
        s
          << "Competency will change from "
          << Competencies().ToStr(competency_before)
          << " to "
          << Competencies().ToStr(competency_after)
          << '\n'
        ;
        TRACE(s.str());
      }
      if (is_complex_changed)
      {
        std::stringstream s;
        s << "IsComplex will change from " << is_complex_before
          << " to " << is_complex_after << '\n';
        TRACE(s.str());
      }
      if (is_concrete_changed)
      {
        std::stringstream s;
        s << "IsConcrete will change from " << is_concrete_before
          << " to " << is_concrete_after << '\n';
        TRACE(s.str());
      }
      if (is_specific_changed)
      {
        std::stringstream s;
        s << "IsSpecific will change from " << is_specific_before
          << " to " << is_specific_after << '\n';
        TRACE(s.str());
      }
      if (text_changed)
      {
        std::stringstream s;
        s << "Text will change from '" << text_before
          << "' to '" << text_after << "'\n";
        TRACE(s.str());
      }
    }

    //Disconnect m_example
    m_example->m_signal_competency_changed.disconnect(
      boost::bind(&ribi::cmap::QtExampleDialog::OnCompetencyChanged,this,boost::lambda::_1)
    );
    m_example->m_signal_is_complex_changed.disconnect(
      boost::bind(&ribi::cmap::QtExampleDialog::OnIsComplexChanged,this,boost::lambda::_1)
    );
    m_example->m_signal_is_concrete_changed.disconnect(
      boost::bind(&ribi::cmap::QtExampleDialog::OnIsConcreteChanged,this,boost::lambda::_1)
    );
    m_example->m_signal_is_specific_changed.disconnect(
      boost::bind(&ribi::cmap::QtExampleDialog::OnIsSpecificChanged,this,boost::lambda::_1)
    );
    m_example->m_signal_text_changed.disconnect(
      boost::bind(&ribi::cmap::QtExampleDialog::OnTextChanged,this,boost::lambda::_1)
    );
  }

  //Replace m_example by the new one
  m_example = example;


  assert(m_example->GetCompetency() == competency_after );
  assert(m_example->GetIsComplex()  == is_complex_after );
  assert(m_example->GetIsConcrete() == is_concrete_after);
  assert(m_example->GetIsSpecific() == is_specific_after);
  assert(m_example->GetText()       == text_after       );

  m_example->m_signal_competency_changed.connect(
    boost::bind(&ribi::cmap::QtExampleDialog::OnCompetencyChanged,this,boost::lambda::_1)
  );
  m_example->m_signal_is_complex_changed.connect(
    boost::bind(&ribi::cmap::QtExampleDialog::OnIsComplexChanged,this,boost::lambda::_1)
  );
  m_example->m_signal_is_concrete_changed.connect(
    boost::bind(&ribi::cmap::QtExampleDialog::OnIsConcreteChanged,this,boost::lambda::_1)
  );
  m_example->m_signal_is_specific_changed.connect(
    boost::bind(&ribi::cmap::QtExampleDialog::OnIsSpecificChanged,this,boost::lambda::_1)
  );
  m_example->m_signal_text_changed.connect(
    boost::bind(&ribi::cmap::QtExampleDialog::OnTextChanged,this,boost::lambda::_1)
  );

  //Emit everything that has changed
  if (competency_changed)
  {
    m_example->m_signal_competency_changed(m_example.get());
  }
  if (is_complex_changed)
  {
    m_example->m_signal_is_complex_changed(m_example.get());
  }
  if (is_concrete_changed)
  {
    m_example->m_signal_is_concrete_changed(m_example.get());
  }
  if (is_specific_changed)
  {
    m_example->m_signal_is_specific_changed(m_example.get());
  }
  if (text_changed)
  {
    m_example->m_signal_text_changed(m_example.get());
  }

  this->setMinimumHeight(GetMinimumHeight(*m_example));

  assert( example ==  m_example);
  assert(*example == *m_example);
}

void ribi::cmap::QtExampleDialog::OnCompetencyChanged(const Example * const example)
{
  const bool verbose{false};
  assert(example);

  const int index_before = ui->box_competency->currentIndex();
  const int index_after = Competencies().ToIndex(example->GetCompetency());
  assert(index_after >= 0);
  assert(index_after < static_cast<int>(Competencies().GetAllCompetencies().size()));

  if (verbose)
  {
    std::stringstream s;
    s << "Change competency index from " << index_before << " to " << index_after;
    TRACE(s.str());
  }

  ui->box_competency->setCurrentIndex(index_after);
  assert(ui->box_competency->currentIndex() == index_after);
}

void ribi::cmap::QtExampleDialog::OnIsComplexChanged(const Example * const example)
{
  assert(example);
  ui->box_is_complex->setChecked(example->GetIsComplex());
}

void ribi::cmap::QtExampleDialog::OnIsConcreteChanged(const Example * const example)
{
  assert(example);
  ui->box_is_concrete->setChecked(example->GetIsConcrete());
}

void ribi::cmap::QtExampleDialog::OnIsSpecificChanged(const Example * const example)
{
  assert(example);
  ui->box_is_specific->setChecked(example->GetIsSpecific());
}

void ribi::cmap::QtExampleDialog::OnTextChanged(const Example * const example)
{
  assert(example);
  ui->edit_text->setText(example->GetText().c_str());
}

#ifndef NDEBUG
void ribi::cmap::QtExampleDialog::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  ExampleFactory();
  const TestTimer test_timer(__func__,__FILE__,1.0);
  QtExampleDialog d;
}
#endif

void ribi::cmap::QtExampleDialog::on_box_competency_currentIndexChanged(int index)
{
  const bool verbose{false};

  if (!m_example)
  {
    //Used in construction
    return;
  }

  assert(index >= 0);
  assert(index < static_cast<int>(Competencies().GetAllCompetencies().size()));
  const auto competency = Competencies().GetAllCompetencies()[index];

  if (verbose)
  {
    std::stringstream s;
    s << "QtExampleDialog will set competency " << Competencies().ToStr(competency)
      << " (index " << index << ", current competency is "
      << (m_example ? Competencies().ToStr(m_example->GetCompetency()) : "[nullptr]")
      << ")";
    TRACE(s.str());
  }
  //Let the Example figure out itself if this changes anything;
  //Allow setting a new competency if it equals the current
  m_example->SetCompetency(competency);

  assert(m_example->GetCompetency() == competency);
}

void ribi::cmap::QtExampleDialog::on_box_is_complex_stateChanged(int)
{
  m_example->SetIsComplex(ui->box_is_complex->isChecked());
}

void ribi::cmap::QtExampleDialog::on_box_is_concrete_stateChanged(int)
{
  m_example->SetIsConcrete(ui->box_is_concrete->isChecked());
}

void ribi::cmap::QtExampleDialog::on_box_is_specific_stateChanged(int)
{
  m_example->SetIsSpecific(ui->box_is_specific->isChecked());
}

void ribi::cmap::QtExampleDialog::on_edit_text_textChanged(const QString &arg1)
{
  m_example->SetText(arg1.toStdString());
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapexamplesdialog.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPEXAMPLESDIALOG_H
#define QTCONCEPTMAPEXAMPLESDIALOG_H

#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include "qthideandshowdialog.h"

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace Ui { class QtExamplesDialog; }

namespace ribi {
namespace cmap {

///Displays and modifies Examples
class QtExamplesDialog : public ribi::QtHideAndShowDialog
{
  Q_OBJECT

public:
  explicit QtExamplesDialog(QWidget *parent = 0);
  QtExamplesDialog(const QtExamplesDialog&) = delete;
  QtExamplesDialog& operator=(const QtExamplesDialog&) = delete;
  ~QtExamplesDialog();

  void SetExamples(const boost::shared_ptr<Examples>& examples);
  boost::shared_ptr<Examples> GetExamples() const noexcept { return m_examples; }

  ///Something of one of the examples was changed
  mutable boost::signals2::signal<void(QtExamplesDialog*)> m_signal_qtexamplesdialog_changed;

  static int GetMinimumHeight(const Examples& examples) noexcept;

private:
  Ui::QtExamplesDialog *ui;
  std::vector<boost::shared_ptr<QtExampleDialog>> m_dialogs;
  boost::shared_ptr<Examples> m_examples;

  void OnExamplesChanged(Examples* const examples) noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

};

} //~namespace ribi
} //~namespace cmap

#endif // QTCONCEPTMAPEXAMPLESDIALOG_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapexamplesdialog.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#include "qtconceptmapexamplesdialog.h"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <cassert>

#include <boost/bind/bind.hpp>
#include <boost/lambda/lambda.hpp>

#include <QLabel>
#include <QLayout>
#include <QVBoxLayout>

#include "conceptmapexample.h"
#include "conceptmapexamples.h"
#include "conceptmapexamplesfactory.h"
#include "qtconceptmapexampledialog.h"
#include "testtimer.h"
#include "trace.h"
#include "ui_qtconceptmapexamplesdialog.h"
#pragma GCC diagnostic pop

ribi::cmap::QtExamplesDialog::QtExamplesDialog(QWidget *parent)
  : QtHideAndShowDialog(parent),
    m_signal_qtexamplesdialog_changed{},
    ui(new Ui::QtExamplesDialog),
    m_dialogs{},
    m_examples{}
{
  ui->setupUi(this);
  #ifndef NDEBUG
  Test();
  #endif
}

ribi::cmap::QtExamplesDialog::~QtExamplesDialog()
{
  delete ui;
}

int ribi::cmap::QtExamplesDialog::GetMinimumHeight(const Examples& examples) noexcept
{
  int height = 0;
  for (const auto example: examples.Get())
  {
    const int margin = 16;
    height += QtExampleDialog::GetMinimumHeight(*example);
    height += margin;
  }
  return height;
}

void ribi::cmap::QtExamplesDialog::OnExamplesChanged(Examples* examples) noexcept
{
  const bool verbose{false};

  //if (examples == m_examples.get()) return; //Will allways be true

  //Check if the dialog needs to change
  {
    bool will_change = false;
    if (examples->Get().size() != m_dialogs.size())
    {
      will_change = true;
    }
    else
    {
      const int n = static_cast<int>(examples->Get().size());
      for (int i=0; i!=n; ++i)
      {
        if (m_dialogs[i]->GetExample() != examples->Get()[i])
        {
          will_change = true;
        }
      }
    }
    if (!will_change) return;
  }

  //Creating the right number of QtExampleDialog instances
  assert(examples);
  while (examples->Get().size() < m_dialogs.size())
  {
    //Need to remove m_dialogs
    assert(layout());
    layout()->removeWidget(m_dialogs.back().get());
    m_dialogs.pop_back();

    if (verbose)
    {
      std::stringstream s;
      s << "QtExamplesDialog removed an Example instance";
      TRACE(s.str());
    }
  }
  while (examples->Get().size() > m_dialogs.size())
  {
    boost::shared_ptr<QtExampleDialog> dialog(new QtExampleDialog);
    assert(layout());
    layout()->addWidget(dialog.get());
    m_dialogs.push_back(dialog);

    if (verbose)
    {
      std::stringstream s;
      s << "QtExamplesDialog added an Example instance";
      TRACE(s.str());
    }
  }
  assert(examples->Get().size() == m_dialogs.size());


  const int n = static_cast<int>(m_dialogs.size());
  for (int i=0; i!=n; ++i)
  {
    assert(m_dialogs[i]);
    assert(examples->Get()[i]);
    m_dialogs[i]->SetExample(examples->Get()[i]);
    if (verbose)
    {
      std::stringstream s;
      s << "QtExamplesDialog will set Example '" << examples->Get()[i]->ToStr() << "'\n";
      TRACE(s.str());
    }
    assert( examples->Get()[i] ==  m_dialogs[i]->GetExample());
    assert(*examples->Get()[i] == *m_dialogs[i]->GetExample());
  }

}

void ribi::cmap::QtExamplesDialog::SetExamples(const boost::shared_ptr<Examples>& examples)
{
  //const bool verbose{false};
  assert(examples);

  if (m_examples == examples) return;

  bool examples_changed = true;
  if (m_examples)
  {
    examples_changed = *m_examples != *examples;

    //Disconnect m_examples
    m_examples->m_signal_examples_changed.disconnect(
      boost::bind(&ribi::cmap::QtExamplesDialog::OnExamplesChanged,this,boost::lambda::_1)
    );
  }

  //Replace m_example by the new one
  m_examples = examples;

  m_examples->m_signal_examples_changed.connect(
    boost::bind(&ribi::cmap::QtExamplesDialog::OnExamplesChanged,this,boost::lambda::_1)
  );
  //Emit that everything has changed
  if (examples_changed)
  {
    //For those interested in m_examples
    m_examples->m_signal_examples_changed(m_examples.get());

    //For those interested in this dialog
    m_signal_qtexamplesdialog_changed(this);
  }

  assert( m_examples ==  examples);
  assert(*m_examples == *examples);
  this->setMinimumHeight(GetMinimumHeight(*m_examples));
}

#ifndef NDEBUG
void ribi::cmap::QtExamplesDialog::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  QtExampleDialog();
  const TestTimer test_timer(__func__,__FILE__,1.0);
  QtExamplesDialog d;
  for(const auto examples: ExamplesFactory().GetTests())
  {
    d.SetExamples(examples);
    assert(d.GetExamples() == examples);
  }
}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmapexamplesitem.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPEXAMPLESITEM_H
#define QTCONCEPTMAPEXAMPLESITEM_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include "qtroundededitrectitem.h"
#include "qtconceptmapfwd.h"
#include "qtconceptmapqtedge.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {


///QtExamplesItem displays an Examples
struct QtExamplesItem : public QtRoundedEditRectItem
{
  explicit QtExamplesItem(QGraphicsItem* parent = 0);
  QtExamplesItem(const QtExamplesItem&) = delete;
  QtExamplesItem& operator=(const QtExamplesItem&) = delete;
  ~QtExamplesItem() noexcept {}
  ///Check the buddy item
  const QGraphicsItem* GetBuddyItem() const noexcept { return m_item; }

  ///Set the concept this item displays the examples of.
  ///If the concept is nullptr, this item hides
  void SetBuddyItem(const QGraphicsItem* const item);

  ///Request update of QGraphicsScene, because this item has changed
  mutable boost::signals2::signal<void() > m_signal_request_scene_update;
  ///The competency, as might be judged by an assessor

protected:

  void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) noexcept;

private:

  ///The concept this item displays the examples of.
  ///If m_concept is nullptr, this item hides
  ///m_item == m_edge if it can be cast, else m_edge == nullptr
  ///m_item == m_node if it can be cast, else m_node == nullptr
  const QGraphicsItem * m_item;
  const QtEdge * m_qtedge; //
  const QtNode * m_qtnode;

  ///Item has updated, Examples must follow
  void OnItemUpdated( /*const QGraphicsItem * const item*/ );

  void SetExamples(const boost::shared_ptr<const Examples>& examples);

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPEXAMPLESITEM_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapexamplesitem.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapexamplesitem.h"

#include <boost/lambda/lambda.hpp>

#include <QFont>
#include <QPainter>
#include <QGraphicsScene>

#include "conceptmapconcept.h"
#include "conceptmapexample.h"
#include "conceptmapexamples.h"
#include "conceptmaphelper.h"
#include "conceptmapnode.h"
#include "qtconceptmapbrushfactory.h"
#include "qtconceptmapqtedge.h"
#include "qtconceptmapqtnode.h"
#include "qtconceptmapelement.h"
#include "qtconceptmapelement.h"
#include "qtitemdisplaystrategy.h"
#include "testtimer.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::cmap::QtExamplesItem::QtExamplesItem(
  QGraphicsItem* parent)
  : QtRoundedEditRectItem(
      { "..." },
      QtRoundedEditRectItem::Padding(),
      QFont("monospace",9),
      parent
    ),
    m_signal_request_scene_update{},
    m_item{nullptr},
    m_qtedge{nullptr},
    m_qtnode{nullptr}
{
  #ifndef NDEBUG
  Test();
  #endif
  this->setPen(QPen(QColor(255,0,0)));
  this->setBrush(QtBrushFactory::CreateWhiteGradientBrush());
  this->setFlags(0);
  this->setZValue(2.0);
  this->setVisible(false);
  //this->SetBuddyItem(concept);
}

void ribi::cmap::QtExamplesItem::OnItemUpdated()
{
  this->update();
  this->m_signal_request_scene_update();
}

void ribi::cmap::QtExamplesItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) noexcept
{
  //this->SetExamples(this->m_item->GetNode()->GetConcept()->GetExamples());
  const auto qtnode = m_qtedge ? m_qtedge->GetQtNode().get() : m_qtnode;

  const QPointF p = qtnode->GetCenterPos();
  const auto w = qtnode->GetOuterWidth();
  const auto h = qtnode->GetOuterHeight();
  const QRectF r(-0.5 * w,-0.5 * h,0.5 * w,0.5 * h);
  this->SetCenterPos(
    p.x() + (0.5 * r.width() ) + 4.0 + (0.5 * GetInnerWidth() ),
    p.y() + (0.5 * r.height()) + 4.0 + (0.5 * GetInnerHeight())
  );

  QtRoundedEditRectItem::paint(painter,option,widget);
}

void ribi::cmap::QtExamplesItem::SetBuddyItem(const QGraphicsItem * const item)
{
  if (m_item != item)
  {
    m_item = item;
    m_qtedge = dynamic_cast<const QtEdge*>(item);
    m_qtnode = dynamic_cast<const QtNode*>(item);
    if (m_qtedge && !m_qtedge->GetQtNode()->GetNode()->GetConcept()->GetExamples()->Get().empty())
    {
      m_qtedge->m_signal_edge_changed.connect(
        boost::bind(
          &ribi::cmap::QtExamplesItem::OnItemUpdated,this // ,boost::lambda::_1
        )
      );
      this->SetExamples(m_qtedge->GetQtNode()->GetNode()->GetConcept()->GetExamples());
      this->setVisible(true);
    }
    if (m_qtnode && !m_qtnode->GetNode()->GetConcept()->GetExamples()->Get().empty())
    {
      m_qtnode->m_signal_node_changed.connect(
        boost::bind(
          &ribi::cmap::QtExamplesItem::OnItemUpdated,this //,boost::lambda::_1
        )
      );
      this->SetExamples(m_qtnode->GetNode()->GetConcept()->GetExamples());
      this->setVisible(true);
    }
    m_signal_request_scene_update();
  }
  else
  {
    if (this->isVisible())
    {
      this->setVisible(false);
      m_signal_request_scene_update();
    }
  }
}

void ribi::cmap::QtExamplesItem::SetExamples(const boost::shared_ptr<const ribi::cmap::Examples>& examples)
{
  std::vector<std::string> v;
  for (const boost::shared_ptr<const Example>& example: examples->Get())
  {
    const std::string s {example->GetText()};
    const std::size_t wordwrap_length{40};
    const std::vector<std::string> w {Wordwrap(s,wordwrap_length)};
    std::copy(w.begin(),w.end(),std::back_inserter(v));
  }
  this->SetText(v);
}

#ifndef NDEBUG
void ribi::cmap::QtExamplesItem::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  QtRoundedEditRectItem();

  const TestTimer test_timer(__func__,__FILE__,1.0);
}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmapfwd.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPFWD_H
#define QTCONCEPTMAPFWD_H

#include "conceptmapfwd.h"

namespace ribi {

struct QtQuadBezierArrowItem;
struct QtRoundedRectItemDialog;
struct QtRoundedEditRectItem;
struct QtRoundedEditRectItemDialog;
struct QtKeyboardFriendlyGraphicsView;

namespace cmap {

struct QtConceptDialog;
struct QtConceptMap;
struct QtConceptMapElement;
struct QtConceptMapWidget;
struct QtDisplayConceptMap;
struct QtDisplayStrategy;
struct QtEdge1;
struct QtEdge2;
struct QtEdgeDialog;
struct QtEditConceptMap;
//struct QtEditStrategy;
struct QtExampleDialog;
struct QtExamplesDialog;
struct QtExamplesItem;
//struct QtItemDisplayStrategy;
struct QtItemHighlighter;
struct QtNewArrow;
struct QtNode;
struct QtNodeDialog;   //QtDialog showing a Node
struct QtQtEdgeDialog; //QtDialog showing a QtEdge
struct QtQtNodeDialog; //QtDialog showing a QtNode
struct QtRateConceptMap;
struct QtRateStrategy;
struct QtTool;

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPFWD_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapitemhighlighter.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTHIGHLIGHTER_H
#define QTHIGHLIGHTER_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include <QObject>
#include <QTimer>

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace ribi {

namespace cmap {

class QtItemHighlighter : public QObject
{
  Q_OBJECT

public:
  QtItemHighlighter(QObject *parent = 0);
  QtItemHighlighter(const QtItemHighlighter&) = delete;
  QtItemHighlighter& operator=(const QtItemHighlighter&) = delete;

  ///Get the item being highlighted
  ///Returns nullptr if no item is highlighted
        QtNode* GetItem()       noexcept { return m_item; }
  const QtNode* GetItem() const noexcept { return m_item; }

  ///Set the item to highlight.
  ///If set with a nullptr, highlighting the current item stops
  ///If set with the same item twice, nothing new happens
  void SetItem(QtNode* const item);

private:
  ///A simple counter
  int m_cnt;

  ///The item being highlighted
  QtNode* m_item;

  ///The timer used for highlighting every 100 ms
  QTimer * const m_timer;

private slots:
  void OnTimer();
};

} //~namespace cmap

} //~namespace ribi

#endif // QTHIGHLIGHTER_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapitemhighlighter.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapitemhighlighter.h"

#include <boost/math/constants/constants.hpp>

#include <QGraphicsItem>
#include <QTimer>
//#include "qtconceptmapconceptitem.h"
#include "qtconceptmapqtnode.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::cmap::QtItemHighlighter::QtItemHighlighter(QObject *parent)
  : QObject(parent),
    m_cnt(0),
    m_item(nullptr),
    m_timer(new QTimer(this))
{
  m_timer->setInterval(10); //ms
  QObject::connect(
    m_timer,
    &QTimer::timeout,
    this,
    &ribi::cmap::QtItemHighlighter::OnTimer
  );
}

void ribi::cmap::QtItemHighlighter::SetItem(QtNode* const item)
{
  if (m_item == item) return;

  if (item)
  {
    this->m_timer->start();
  }
  else
  {
    ///Set the current item in its normal rotation
    if (m_item) m_item->setRotation(0.0);
    this->m_timer->stop();
  }
  m_item = item;
}


void ribi::cmap::QtItemHighlighter::OnTimer()
{
  if (m_item)
  {
    ++m_cnt;
    #define USE_ROTATION_ANIMATION_675982734653425297529
    #ifdef USE_MOVING_POSITION_ANIMATION_723497235973450873659360843975308
    QPointF pos = m_item->pos();
    switch (m_cnt % 4)
    {
      case 0: pos.setX(pos.x() + 2.0); break;
      case 1: pos.setY(pos.y() + 2.0); break;
      case 2: pos.setX(pos.x() - 2.0); break;
      case 3: pos.setY(pos.y() - 2.0); break;
      default: assert(!"Should not get here");
    }
    m_item->setPos(pos);
    #endif
    #ifdef USE_ROTATION_ANIMATION_675982734653425297529
    const double pi = boost::math::constants::pi<double>();
    const double angle = 2.0 * pi * static_cast<double>(m_cnt) / 360.0;
    const double speed_up = 5.0;
    const double r = std::sin(angle * speed_up) * 5.0;
    m_item->setRotation(r);
    #endif
    m_item->update();
  }
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapnewarrow.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPNEWARROW_H
#define QTCONCEPTMAPNEWARROW_H

#include <string>
#include <vector>


#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <QBrush>
#include "qtarrowitem.h"
#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

///QtNewArrow is the arrow shown before a new one is added
struct QtNewArrow : public QtArrowItem
{
  QtNewArrow(
    QtNode * const from,
    const QPointF& current_to);
  QtNewArrow(const QtNewArrow&) = delete;
  QtNewArrow& operator=(const QtNewArrow&) = delete;
  ///Obtain the source node
  const QtNode * GetFrom() const { return m_from; }
        QtNode * GetFrom()       { return m_from; }

  private:
  ///Must be suppplied
  void paint(QPainter* painter, const QStyleOptionGraphicsItem* option = 0, QWidget* widget = 0);

  ///The source node
  ///Cannot be const as the user might want to edit it
  QtNode * const m_from;
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPNEWARROW_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapnewarrow.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapnewarrow.h"

#include <string>
#include <boost/lexical_cast.hpp>

#include <QFont>
#include <QPainter>

//#include "qtconceptmapconceptitem.h"
#include "qtconceptmapqtnode.h"
#include "trace.h"

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#pragma GCC diagnostic pop

///Obtain the zero or one intersections between two finite lines
//From http://www.richelbilderbeek.nl/CppGetLineLineIntersections.htm
template <class T>
const std::vector<
  boost::geometry::model::d2::point_xy<T>
>
GetLineLineIntersections(
  const boost::geometry::model::linestring<
    boost::geometry::model::d2::point_xy<T>
  > line1,
  const boost::geometry::model::linestring<
    boost::geometry::model::d2::point_xy<T>
  > line2)
{
  typedef boost::geometry::model::d2::point_xy<T> Point;
  //typedef boost::geometry::model::linestring<Point> Line;
  std::vector<Point> points;
  boost::geometry::intersection(line1,line2,points);
  assert(points.empty() || points.size() == 1);
  return points;
}

//Helper function to create a const line without a temporary std::vector
template <class T>
const boost::geometry::model::linestring<boost::geometry::model::d2::point_xy<T>
>
CreateLine(const std::vector<boost::geometry::model::d2::point_xy<T> >& v)
{
  return boost::geometry::model::linestring<
    boost::geometry::model::d2::point_xy<T>
  >(v.begin(),v.end());
}

///Obtain the zero, one or two intersections between a line and a rectanle
//From http://www.richelbilderbeek.nl/CppGetLineRectIntersections.htm
template <class T>
const std::vector<
  boost::geometry::model::d2::point_xy<T>
>
GetLineRectIntersections(
  const boost::geometry::model::linestring<
    boost::geometry::model::d2::point_xy<T>
  > line,
  const boost::geometry::model::box<
    boost::geometry::model::d2::point_xy<T>
  > rect)
{
  typedef boost::geometry::model::d2::point_xy<T> Point;
  typedef boost::geometry::model::linestring<Point> Line;
  //typedef boost::geometry::model::box<Point> Rect;

  const Point p0 = Point(rect.min_corner().x(), rect.min_corner().y());
  const Point p1 = Point(rect.max_corner().x(), rect.min_corner().y());
  const Point p2 = Point(rect.min_corner().x(), rect.max_corner().y());
  const Point p3 = Point(rect.max_corner().x(), rect.max_corner().y());
  const std::vector<Line> lines
    =
    {
      CreateLine(std::vector<Point>( {p0,p1} )),
      CreateLine(std::vector<Point>( {p0,p2} )),
      CreateLine(std::vector<Point>( {p1,p3} )),
      CreateLine(std::vector<Point>( {p2,p3} ))
    };
  std::vector<Point> points;
  std::for_each(lines.begin(),lines.end(),
    [&points,line](const Line& side)
    {
      const std::vector<Point> v = GetLineLineIntersections(line,side);
      std::copy(v.begin(),v.end(),std::back_inserter(points));
    }
  );
  //Remove doublures
  //Put 'typename' before 'std::vector<Point>::iteratortype' to prevent getting the error below:
  //error: need 'typename' before 'std::vector<boost::geometry::model::d2::point_xy<T> >::iterator'
  //  because 'std::vector<boost::geometry::model::d2::point_xy<T> >' is a dependent scope
  typename std::vector<Point>::iterator new_end = std::unique( points.begin(),points.end(),
    [](const Point& lhs, const Point& rhs)
    {
      return lhs.x() == rhs.x() && lhs.y() == rhs.y();
    }
  );
  points.erase(new_end,points.end());

  assert(points.size() <= 2);

  return points;
}

ribi::cmap::QtNewArrow::QtNewArrow(
  QtNode * const from,
  const QPointF& current_to)
  : QtArrowItem(
      from->GetCenterX(),
      from->GetCenterY(),
      false,
      current_to.x(),
      current_to.y(),
      true
    ),
    m_from(from)
{
  assert(m_from);

  //New arrows are above all items
  this->setZValue(2.0);

  //A new arrow must not be moveable
  this->setFlag(QGraphicsItem::ItemIsSelectable,false);
  this->setFlag(QGraphicsItem::ItemIsMovable,false);

  //Reject enterHoverEvents
  this->setAcceptHoverEvents(false);

  assert(!(this->flags() & QGraphicsItem::ItemIsSelectable));
  assert(!(this->flags() & QGraphicsItem::ItemIsMovable ));
}

void ribi::cmap::QtNewArrow::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
  typedef boost::geometry::model::d2::point_xy<double> Point;
  typedef boost::geometry::model::linestring<Point> Line;
  typedef boost::geometry::model::box<Point> Rect;
  const Line line = CreateLine(
    std::vector<Point>(
      {
        Point(m_from->GetCenterX(),m_from->GetCenterY()),
        Point(this->line().p2().x(),this->line().p2().y())
      }
    )
  );
  const QRectF qr1 = m_from->boundingRect().translated(m_from->GetCenterPos());

  const Rect r1(
    Point(qr1.topLeft().x()    ,qr1.topLeft().y()    ),
    Point(qr1.bottomRight().x(),qr1.bottomRight().y())
    );

  std::vector<Point> p1 = GetLineRectIntersections(line,r1);
  if (p1.empty())
  {
    //Yes,it happens, when the line does not leave the rectangle
    //this happens when the two node rectanges overlap
    p1.push_back(Point(m_from->GetCenterX(),m_from->GetCenterY()));
  }
  assert(!p1.empty());
  assert(p1.size() == 1);
  const QPointF p2 = this->line().p2();
  this->setLine(QLineF(QPointF(p1[0].x(),p1[0].y()),p2));
  QtArrowItem::paint(painter,option,widget);
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapnodedialog.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPNODEDIALOG_H
#define QTCONCEPTMAPNODEDIALOG_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace Ui { class QtNodeDialog; }

namespace ribi {
namespace cmap {

///Displays and modifies a Node
class QtNodeDialog : public ribi::QtHideAndShowDialog
{
  Q_OBJECT

public:
  explicit QtNodeDialog(QWidget *parent = 0);
  QtNodeDialog(const QtNodeDialog&) = delete;
  QtNodeDialog& operator=(const QtNodeDialog&) = delete;
  ~QtNodeDialog();

  static int GetMinimumHeight(const Node& node) noexcept;
  boost::shared_ptr<Node> GetNode() const noexcept { return m_node; }
  std::string GetUiName() const noexcept;
  ///Read the X value directly from GUI
  double GetUiX() const noexcept;
  ///Read the Y value directly from GUI
  double GetUiY() const noexcept;

  void SetNode(const boost::shared_ptr<Node>& node) noexcept;

  ///Set the name directly to GUI
  void SetUiName(const std::string& name) noexcept;
  ///Set the X value directly to GUI
  void SetUiX(const double x) noexcept;
  ///Set the Y value directly to GUI
  void SetUiY(const double y) noexcept;


private slots:
  void on_box_x_valueChanged(double arg1);
  void on_box_y_valueChanged(double arg1);

private:
  Ui::QtNodeDialog *ui;

  ///The Node to work on
  boost::shared_ptr<Node> m_node;

  boost::shared_ptr<QtConceptDialog> m_qtconceptdialog;


  //concept is non-const, as its displayal by this dialog renders it editable
  void OnConceptChanged(Node * const node);
  void OnXchanged(Node * const node);
  void OnYchanged(Node * const node);

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPNODEDIALOG_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapnodedialog.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapnodedialog.h"

#include <cassert>
#include <boost/bind/bind.hpp>
#include <boost/lambda/lambda.hpp>

#include "conceptmapcompetencies.h"
#include "conceptmapconcept.h"
#include "conceptmapconceptfactory.h"
#include "conceptmapexamples.h"
#include "conceptmapnode.h"
#include "conceptmapnodefactory.h"
#include "qtconceptmapconceptdialog.h"
#include "qtconceptmapexamplesdialog.h"
#include "ui_qtconceptmapnodedialog.h"
#include "testtimer.h"
#include "trace.h"

#pragma GCC diagnostic pop

ribi::cmap::QtNodeDialog::QtNodeDialog(QWidget *parent)
  : ribi::QtHideAndShowDialog(parent),
    ui(new Ui::QtNodeDialog),
    m_node{},
    m_qtconceptdialog{new QtConceptDialog}
{
  ui->setupUi(this);
  #ifndef NDEBUG
  Test();
  #endif

  {
    assert(layout());
    layout()->addWidget(m_qtconceptdialog.get());

  }
}

ribi::cmap::QtNodeDialog::~QtNodeDialog()
{
  delete ui;
}

int ribi::cmap::QtNodeDialog::GetMinimumHeight(const Node& node) noexcept
{
  return QtConceptDialog::GetMinimumHeight(*node.GetConcept()) + 82;
}


double ribi::cmap::QtNodeDialog::GetUiX() const noexcept
{
  return ui->box_x->value();
}

double ribi::cmap::QtNodeDialog::GetUiY() const noexcept
{
  return ui->box_y->value();
}

std::string ribi::cmap::QtNodeDialog::GetUiName() const noexcept
{
  return m_qtconceptdialog->GetUiName();
}

void ribi::cmap::QtNodeDialog::SetNode(const boost::shared_ptr<Node>& node) noexcept
{
  const bool verbose{false};

  assert(node);
  if (m_node == node)
  {

    return;
  }

  if (verbose)
  {
    std::stringstream s;
    s << "Setting node '" << node->ToStr() << "'\n";
  }
  const auto concept_after = node->GetConcept();
  const auto x_after = node->GetX();
  const auto y_after = node->GetY();

  bool concept_changed  = true;
  bool x_changed  = true;
  bool y_changed = true;

  if (m_node)
  {
    const auto concept_before = m_node->GetConcept();
    const auto x_before = m_node->GetX();
    const auto y_before = m_node->GetY();

    concept_changed = concept_before != concept_after;
    x_changed = x_before != x_after;
    y_changed = y_before != y_after;


    if (verbose)
    {
      if (concept_changed)
      {
        std::stringstream s;
        s
          << "Concept will change from "
          << concept_before->ToStr()
          << " to "
          << concept_after->ToStr()
          << '\n'
        ;
        TRACE(s.str());
      }
      if (x_changed)
      {
        std::stringstream s;
        s << "X will change from " << x_before
          << " to " << x_after << '\n';
        TRACE(s.str());
      }
      if (y_changed)
      {
        std::stringstream s;
        s << "Y will change from " << y_before
          << " to " << y_after << '\n';
        TRACE(s.str());
      }
    }
    //Disconnect m_concept
    m_node->m_signal_concept_changed.disconnect(
      boost::bind(&ribi::cmap::QtNodeDialog::OnConceptChanged,this,boost::lambda::_1)
    );
    m_node->m_signal_x_changed.disconnect(
      boost::bind(&ribi::cmap::QtNodeDialog::OnXchanged,this,boost::lambda::_1)
    );
    m_node->m_signal_y_changed.disconnect(
      boost::bind(&ribi::cmap::QtNodeDialog::OnYchanged,this,boost::lambda::_1)
    );
  }

  //Replace m_example by the new one
  m_node = node;


  assert(m_node->GetConcept() == concept_after );
  assert(m_node->GetX() == x_after );
  assert(m_node->GetY() == y_after);

  m_node->m_signal_concept_changed.connect(
    boost::bind(&ribi::cmap::QtNodeDialog::OnConceptChanged,this,boost::lambda::_1)
  );
  m_node->m_signal_x_changed.connect(
    boost::bind(&ribi::cmap::QtNodeDialog::OnXchanged,this,boost::lambda::_1)
  );
  m_node->m_signal_y_changed.connect(
    boost::bind(&ribi::cmap::QtNodeDialog::OnYchanged,this,boost::lambda::_1)
  );

  //Emit everything that has changed
  if (concept_changed)
  {
    m_node->m_signal_concept_changed(m_node.get());
  }
  if (x_changed)
  {
    m_node->m_signal_x_changed(m_node.get());
  }
  if (y_changed)
  {
    m_node->m_signal_y_changed(m_node.get());
  }

  this->setMinimumHeight(
    this->GetMinimumHeight(
      *m_node
    )
  );

  assert( node ==  m_node);
  assert(*node == *m_node);
}

void ribi::cmap::QtNodeDialog::OnConceptChanged(Node * const node)
{
  const bool verbose{false};
  assert(node);

  const auto concept_before = m_qtconceptdialog->GetConcept();
  const boost::shared_ptr<Concept> concept_after = node->GetConcept();

  if (verbose)
  {
    std::stringstream s;
    s << "Change concept from "
    << (concept_before ? concept_before->ToStr() : "[NONE]")
    << " to " << concept_after->ToStr();
    TRACE(s.str());
  }

  m_qtconceptdialog->SetConcept(concept_after);

  assert(m_qtconceptdialog->GetConcept() == concept_after);
}

void ribi::cmap::QtNodeDialog::OnXchanged(Node * const node)
{
  assert(node);
  ui->box_x->setValue(node->GetX());
}

void ribi::cmap::QtNodeDialog::OnYchanged(Node * const node)
{
  assert(node);
  ui->box_y->setValue(node->GetY());
}

void ribi::cmap::QtNodeDialog::SetUiName(const std::string& name) noexcept
{
  this->m_qtconceptdialog->SetUiName(name);
  assert(GetUiName() == name);
}


void ribi::cmap::QtNodeDialog::SetUiX(const double x) noexcept
{
  ui->box_x->setValue(x);
  //Calls 'on_box_x_valueChanged' automatically
}

void ribi::cmap::QtNodeDialog::SetUiY(const double y) noexcept
{
  ui->box_y->setValue(y);
  //Calls 'on_box_y_valueChanged' automatically
  //ui->box_y does not immediatly update
  //m_node->SetY(y);
  //on_box_y_valueChanged(y);
}


#ifndef NDEBUG
void ribi::cmap::QtNodeDialog::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  NodeFactory().GetTest(1);
  QtConceptDialog();

  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  QtNodeDialog dialog;
  const boost::shared_ptr<Node> node{NodeFactory().GetTest(1)};
  dialog.SetNode(node);
  if (verbose) { TRACE("X of QtNode and QtNodeDialog must match at start"); }
  {
    assert(std::abs(dialog.GetUiX() - node->GetX()) < 2.0);
  }
  if (verbose) { TRACE("If X is set via QtNode, QtNodeDialog must sync"); }
  {
    const double old_x{node->GetX()};
    const double new_x{old_x + 10.0};
    node->SetX(new_x);
    assert(std::abs(new_x - dialog.GetUiX()) < 2.0);
  }
  if (verbose) { TRACE("If X is set via QtNodeDialog, QtNode must sync"); }
  {
    const double old_x{dialog.GetUiX()};
    const double new_x{old_x + 10.0};
    dialog.SetUiX(new_x);
    assert(std::abs(new_x - node->GetX()) < 2.0);
  }
}
#endif

void ribi::cmap::QtNodeDialog::on_box_x_valueChanged(double arg1)
{
  assert(m_node);
  m_node->SetX(arg1);
}

void ribi::cmap::QtNodeDialog::on_box_y_valueChanged(double arg1)
{
  m_node->SetY(arg1);
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedge.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPEDGEITEM_H
#define QTCONCEPTMAPEDGEITEM_H

#include "qtconceptmapqtedge1.h"
#include "qtconceptmapqtedge2.h"

namespace ribi {

struct QtRoundedRectItem;

namespace cmap {

using QtEdge = ribi::cmap::QtEdge1;

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPEDGEITEM_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedge.cpp

 

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedge1.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPEDGEITEM1_H
#define QTCONCEPTMAPEDGEITEM1_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <QGraphicsItem>
#include "qtconceptmapfwd.h"
//#include "qtconceptmapqtedge.h"

#pragma GCC diagnostic pop

namespace ribi {

struct QtRoundedRectItem;

namespace cmap {

///The QtEdge is a QtConceptMapElement that
///draws a curve underneath itself, between head and tail arrowhead
///concept_item is the Strategy for displaying the ConceptItem
//struct QtEdge : public QtConceptMapElement
struct QtEdge1 : public QGraphicsItem
{
  using Base = QGraphicsItem;

  using Arrow = boost::shared_ptr<QtQuadBezierArrowItem>;
  using ReadOnlyArrow = boost::shared_ptr<const QtQuadBezierArrowItem> ;
  using NodePtr = boost::shared_ptr<Node>;
  using ReadOnlyNodePtr = boost::shared_ptr<const Node>;
  using QtNodePtr =  QtNode *;
  using ReadOnlyQtNodePtr = const QtNode *;
  using EdgePtr = boost::shared_ptr<Edge>;
  using ReadOnlyEdgePtr = boost::shared_ptr<const Edge>;

  using From = QtNodePtr;
  using ReadOnlyFrom = ReadOnlyQtNodePtr;
  using To = QtNodePtr;
  using ReadOnlyTo = ReadOnlyQtNodePtr;

  QtEdge1(
    const EdgePtr& edge,
    const From& from,
    const To& to
  );
  QtEdge1(const QtEdge1&) = delete;
  QtEdge1& operator=(const QtEdge1&) = delete;
  ~QtEdge1() noexcept;

  QRectF boundingRect() const override final;

  void DisableAll() noexcept;
  void EnableAll() noexcept;

  ReadOnlyArrow GetArrow() const noexcept { return m_arrow; }
  const Arrow& GetArrow() noexcept { return m_arrow; }

  ReadOnlyEdgePtr GetEdge() const noexcept { return m_edge; }
  EdgePtr GetEdge() noexcept { return m_edge; }

  ///The node item the arrow originates from
  ReadOnlyFrom GetFrom() const noexcept { return m_from; }
  From GetFrom() noexcept { return m_from; }

  ///The node item the arrow targets
  ReadOnlyTo GetTo() const noexcept { return m_to; }
  To GetTo() noexcept { return m_to; }

  boost::shared_ptr<      QtNode> GetQtNode()       noexcept { return m_qtnode; }
  boost::shared_ptr<const QtNode> GetQtNode() const noexcept { return m_qtnode; }

  static std::string GetVersion() noexcept;
  static std::vector<std::string> GetVersionHistory() noexcept;

  void SetEdge(const EdgePtr& edge) noexcept;

  void SetFrom(const From& from) noexcept;

  void SetHasHeadArrow(const bool has_head_arrow) noexcept;
  void SetHasTailArrow(const bool has_tail_arrow) noexcept;

  void SetTo(const To& to) noexcept;

  std::string ToStr() const noexcept;

  mutable boost::signals2::signal<void (QtEdge1 *)> m_signal_base_changed;
  mutable boost::signals2::signal<void (QtEdge1 *)> m_signal_edge_changed;
  mutable boost::signals2::signal<void (QtEdge1 *)> m_signal_focus_in_event;
  mutable boost::signals2::signal<void (QtEdge1 *,const int key)> m_signal_key_down_pressed;

protected:
  /*
  void dragEnterEvent(QGraphicsSceneDragDropEvent *) noexcept override final;
  void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) noexcept override final;
  void dragMoveEvent(QGraphicsSceneDragDropEvent *event) noexcept override final;
  */
  void focusInEvent(QFocusEvent *event) noexcept override final;
  /*
  void hoverMoveEvent(QGraphicsSceneHoverEvent *event) noexcept override final;
  */
  void keyPressEvent(QKeyEvent *event) noexcept override final;
  void mousePressEvent(QGraphicsSceneMouseEvent *event) noexcept override final;
  void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) noexcept override final;
  QPainterPath shape() const noexcept override final;

private:
  ///The arrow used for drawing
  const Arrow m_arrow;

  ///The edge
  boost::shared_ptr<Edge> m_edge;

  ///The node item the arrow originates from
  From m_from;

  ///The QtNode around Edge its Node
  const boost::shared_ptr<QtNode> m_qtnode;

  ///The node item the arrow targets
  To m_to;

  ///Called whenever the edge changes
  void OnEdgeChanged(Edge * const edge) noexcept;
  void OnConceptChanged(Node * const node) noexcept;
  void OnFromChanged(Edge * const edge) noexcept;
  void OnHeadArrowChanged(Edge * const edge) noexcept;
  void OnNodeChanged(Edge * const edge) noexcept;
  void OnNodePosChanged(QtRoundedRectItem * const node) noexcept;
  void OnTailArrowChanged(Edge * const edge) noexcept;
  void OnTextChanged(QtRoundedEditRectItem* item) noexcept;
  void OnToChanged(Edge * const edge) noexcept;

  ///Called whenever the arrow updates
  void OnArrowChanged(const QtQuadBezierArrowItem* const item);

  void OnMustUpdateScene();
  void OnRequestSceneUpdate();

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

std::ostream& operator<<(std::ostream& os, const QtEdge1& qtedge) noexcept;

bool operator==(const QtEdge1& lhs, const QtEdge1& rhs) noexcept;

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPEDGEITEM1_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedge1.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapqtedge1.h"

#include <cassert>

#include <boost/lambda/lambda.hpp>

#include <QCursor>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QKeyEvent>
#include <QPainter>

#include "conceptmapconcept.h"
#include "conceptmapedge.h"
#include "conceptmapnode.h"
#include "container.h"
#include "qtconceptmapqtnode.h"
#include "qtquadbezierarrowitem.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::cmap::QtEdge1::QtEdge1(
    const EdgePtr& edge,
    const From& from,
    const To& to
)
  : m_signal_base_changed{},
    m_signal_edge_changed{},
    m_signal_focus_in_event{},
    m_signal_key_down_pressed{},
    //m_arrow{new QtQuadBezierArrowItem(from,edge->HasTailArrow(),this,edge->HasHeadArrow(),to)},
    m_arrow{nullptr}, //Will be initialized below
    m_edge{}, //Will be initialized by setEdge
    m_from{from},
    m_qtnode{new QtNode(edge->GetNode())},
    m_to{to}
{
  #ifndef NDEBUG
  Test();
  #endif

  //Allow mouse tracking
  //OTOH: must be done by the other thing
  //this->setAcceptHoverEvents(true);

  //const_cast because Arrow constant
  //I just need to have an initialized m_qtnode
  const_cast<Arrow&>(m_arrow).reset(
    new QtQuadBezierArrowItem(
    from,edge->HasTailArrow(),
    this->GetQtNode().get(),
    edge->HasHeadArrow(),
    to
    )
  );

  //QtEdge is just the glue between a collection of things
  /*
  this->setFlags(
      QGraphicsItem::ItemIsFocusable
    | QGraphicsItem::ItemIsMovable
    | QGraphicsItem::ItemIsSelectable
  );
  */

  GetQtNode()->setFlags(
      QGraphicsItem::ItemIsFocusable
    | QGraphicsItem::ItemIsMovable
    | QGraphicsItem::ItemIsSelectable
  );

  GetFrom()->setFlags(
      QGraphicsItem::ItemIsFocusable
    | QGraphicsItem::ItemIsMovable
    | QGraphicsItem::ItemIsSelectable
  );

  GetTo()->setFlags(
      QGraphicsItem::ItemIsFocusable
    | QGraphicsItem::ItemIsMovable
    | QGraphicsItem::ItemIsSelectable
  );

  assert(m_from);
  assert(m_to);
  assert(from != to);
  assert(m_from != m_to);
  //m_edge must be initialized before m_arrow
  //if 'from' or 'to' are CenterNodes, then no item must be put at the center
  //const bool is_connected_to_center_node
  //  = dynamic_cast<QtCenterNode*>(from) || dynamic_cast<QtCenterNode*>(to);
  //if (is_connected_to_center_node)
  //{
  //m_arrow.reset(new QtQuadBezierArrowItem(from,edge->HasTailArrow(),this,edge->HasHeadArrow(),to));
  //}
  //else
  //{
  //  m_arrow.reset(new QtQuadBezierArrowItem(from,edge->HasTailArrow(),this,edge->HasHeadArrow(),to));
  //}
  assert(m_arrow);




  m_arrow->m_signal_item_updated.connect(
    boost::bind(&ribi::cmap::QtEdge1::OnArrowChanged,this,boost::lambda::_1)
  );

  m_qtnode->m_signal_pos_changed.connect(
    boost::bind(&ribi::cmap::QtEdge1::OnNodePosChanged,this,boost::lambda::_1)
  );

  //TODO: Is this a redundant connection? (i.e. a subset of what m_signal_node_changed covers)
  m_qtnode->m_signal_text_changed.connect(
    boost::bind(&ribi::cmap::QtEdge1::OnTextChanged,this,boost::lambda::_1)
  );



  m_from->m_signal_node_changed.connect(
    boost::bind(&ribi::cmap::QtEdge1::OnMustUpdateScene,this)
  );
  m_to->m_signal_node_changed.connect(
    boost::bind(&ribi::cmap::QtEdge1::OnMustUpdateScene,this)
  );

  SetEdge(edge);


  #ifdef TODO_ISSUE_212
  if (!is_connected_to_center_node)
  {
    //Only allow edges not connected to the center node to be edited
    assert(display_strategy);
    QtEditStrategy * const edit_concept = dynamic_cast<QtEditStrategy*>(display_strategy.get());
    if (edit_concept)
    {
      edit_concept->m_signal_request_edit.connect(
        boost::bind(
          &QtConceptMapElement::OnConceptRequestsEdit,
          this
        )
      );
    }
  }
  #else
  //if (QtEditStrategy * edit_concept = dynamic_cast<QtEditStrategy*>(display_strategy.get()))
  //{
  //  edit_concept->m_signal_request_edit.connect(
  //    boost::bind(
  //      &QtConceptMapElement::OnConceptRequestsEdit,
  //      this
  //    )
  //  );
  //}
  #endif

}

ribi::cmap::QtEdge1::~QtEdge1() noexcept
{
  m_arrow->m_signal_item_updated.disconnect(
    boost::bind(&ribi::cmap::QtEdge1::OnArrowChanged,this,boost::lambda::_1)
  );
  m_qtnode->m_signal_text_changed.disconnect(
    boost::bind(&ribi::cmap::QtEdge1::OnTextChanged,this,boost::lambda::_1)
  );

  SetEdge(nullptr);

  //assert(m_from);
  //assert(m_from->m_signal_node_changed.num_slots() > 0);

  //m_from->m_signal_node_changed.disconnect(
  //  boost::bind(&ribi::cmap::QtEdge1::OnMustUpdateScene,this)
  //);
  //assert(m_to);
  //m_to->m_signal_node_changed.disconnect(
  //  boost::bind(&ribi::cmap::QtEdge1::OnMustUpdateScene,this)
  //);


  //Disconnect signals
  /*
  #ifdef TODO_ISSUE_212
  if (!is_connected_to_center_node)
  {
    //Only allow edges not connected to the center node to be edited
    assert(m_display_strategy);
    QtEditStrategy * const edit_concept = dynamic_cast<QtEditStrategy*>(m_display_strategy.get());
    if (edit_concept)
    {
      edit_concept->m_signal_request_edit.disconnect(
        boost::bind(&QtConceptMapElement::OnConceptRequestsEdit,this)
      );
    }
  }
  #else
  if (QtEditStrategy * edit_concept = dynamic_cast<QtEditStrategy*>(display_strategy.get()))
  {
    edit_concept->m_signal_request_edit.disconnect(
      boost::bind(
        &QtConceptMapElement::OnConceptRequestsEdit,
        this
      )
    );
  }
  #endif
  */
}

QRectF ribi::cmap::QtEdge1::boundingRect() const
{


  return m_qtnode->boundingRect().translated(m_qtnode->GetCenterPos())
    .united(m_arrow->boundingRect())
  ;

  //Don't forget to update ::shape if you change ::boundingRect!

  //Some other candidates

  //return m_qtnode->boundingRect()
  //  .united(m_arrow->boundingRect());

  //return m_qtnode->boundingRect().translated(-m_qtnode->GetCenterPos())
  //   .united(m_arrow->boundingRect());

  //return m_qtnode->boundingRect()
  //  .united(m_arrow->boundingRect().translated(m_qtnode->GetCenterPos()));
}

void ribi::cmap::QtEdge1::DisableAll() noexcept
{
  this->setEnabled(false);
  this->setVisible(false);
  this->m_arrow->setEnabled(false);
  this->m_arrow->setVisible(false);
}

void ribi::cmap::QtEdge1::EnableAll() noexcept
{
  this->setEnabled(true);
  this->setVisible(true);
  this->m_arrow->setEnabled(true);
  this->m_arrow->setVisible(true);
}

/*
boost::shared_ptr<const ribi::cmap::Node> ribi::cmap::QtEdge1::GetNode() const noexcept
{
  const auto p = m_edge->GetNode();
  assert(p);
  return p;
}

boost::shared_ptr<ribi::cmap::Node> ribi::cmap::QtEdge1::GetNode() noexcept
{
  const auto p = m_edge->GetNode();
  assert(p);
  return p;
}
*/
std::string ribi::cmap::QtEdge1::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::cmap::QtEdge1::GetVersionHistory() noexcept
{
  return {
    "201x-xx-xx: version 1.0: initial version"
    "2014-08-01: version 1.1: start of versioning"
  };
}

/*
void ribi::cmap::QtEdge1::dragEnterEvent(QGraphicsSceneDragDropEvent *) noexcept
{
  TRACE_FUNC();
  update();
}

void ribi::cmap::QtEdge1::dragLeaveEvent(QGraphicsSceneDragDropEvent *) noexcept
{
  TRACE_FUNC();
  update();
}

void ribi::cmap::QtEdge1::dragMoveEvent(QGraphicsSceneDragDropEvent *) noexcept
{
  TRACE_FUNC();
  //if (scene()) { scene()->update(); }
  update();
}
*/

void ribi::cmap::QtEdge1::focusInEvent(QFocusEvent*) noexcept
{
  m_signal_focus_in_event(this);
  //Lose focus of arrow
  //m_arrow->SetPen(QPen(QColor(0,0,0)));
  //m_display_strategy->SetContourPen(m_display_strategy->GetFocusPen()); //Updates itself
}
/*
void ribi::cmap::QtEdge1::focusOutEvent(QFocusEvent*) noexcept
{
  m_arrow->SetPen(QPen(QColor(0,0,0)));
  //m_display_strategy->SetContourPen(m_display_strategy->GetContourPen()); //Updates itself
}
*/

/*
void ribi::cmap::QtEdge1::hoverMoveEvent(QGraphicsSceneHoverEvent*) noexcept
{
  this->setCursor(QCursor(Qt::PointingHandCursor));
}
*/

void ribi::cmap::QtEdge1::keyPressEvent(QKeyEvent *event) noexcept
{
  assert(m_arrow);
  assert(m_edge);
  m_signal_key_down_pressed(this,event->key());
  /*
  if (m_arrow->isEnabled())
  {
    m_arrow->keyPressEvent(event);
    m_edge->SetHeadArrow( m_arrow->HasHead() );
    m_edge->SetTailArrow( m_arrow->HasTail() );

    assert(m_edge->HasHeadArrow() == m_arrow->HasHead());
    assert(m_edge->HasTailArrow() == m_arrow->HasTail());
  }
  */
  QGraphicsItem::keyPressEvent(event);
}

void ribi::cmap::QtEdge1::mousePressEvent(QGraphicsSceneMouseEvent *event) noexcept
{  
  assert( m_arrow->HasTail() == m_edge->HasTailArrow() );
  assert( m_arrow->HasHead() == m_edge->HasHeadArrow() );
  if (event->modifiers() & Qt::ShiftModifier)
  {
    if ((event->pos() - this->m_arrow->GetTail() + m_qtnode->GetCenterPos()).manhattanLength() < 20.0)
    {
      this->SetHasTailArrow( !m_arrow->HasTail() ); //FIX 2013-02-10
      //this->m_arrow->SetHasTail( !m_arrow->HasTail() ); //BUG 2013-02-10
      //this->update(); //Don't!
      //m_signal_item_updated(this); //Don't!
    }
    else if ((event->pos() - this->m_arrow->GetHead() + m_qtnode->GetCenterPos()).manhattanLength() < 20.0)
    {
      this->SetHasHeadArrow( !m_arrow->HasHead() );
      //this->update(); //Don't!
      //m_signal_item_updated(this); //Don't!
    }
  }

  //What is clicked on: the concept or the arrow? Assume concept
  m_arrow->SetPen(QPen(QColor(0,0,0)));
  if (!m_qtnode->GetInnerRect().contains(event->pos()))
  {
    //If the concept is not clicked...
    //but the arrow is...
    QPointF pos_on_arrow = event->pos();
    pos_on_arrow += (m_qtnode->GetCenterPos());
    if (m_arrow->shape().contains(pos_on_arrow)
      || (event->pos() - this->m_arrow->GetTail() + m_qtnode->GetCenterPos()).manhattanLength() < 20.0
      || (event->pos() - this->m_arrow->GetHead() + m_qtnode->GetCenterPos()).manhattanLength() < 20.0
      )
    {
      //give focus to the arrow
      m_arrow->SetPen(m_arrow->GetFocusPen());
      return;
    }
  }
  //QtConceptMapElement::mousePressEvent(event);
}

void ribi::cmap::QtEdge1::OnConceptChanged(Node * const node) noexcept
{
  //Node changed, sync QtRoundedRectItem
  assert(node);
  assert(node == m_edge->GetNode().get());
  const std::string new_str{node->GetConcept()->GetName()};
  const std::vector<std::string> new_text{new_str};
  assert(new_text.size() == 1);
  m_qtnode->SetText(new_text);
  assert(m_qtnode->GetText() == new_text);
}

void ribi::cmap::QtEdge1::OnEdgeChanged(Edge * const edge) noexcept
{
  OnFromChanged(edge);
  OnNodeChanged(edge);
  OnHeadArrowChanged(edge);
  OnTailArrowChanged(edge);
  OnToChanged(edge);
}


void ribi::cmap::QtEdge1::OnFromChanged(Edge * const edge) noexcept
{
  this->GetFrom()->SetNode(edge->GetFrom());
  assert(edge->GetFrom() == this->GetFrom()->GetNode());
  m_signal_edge_changed(this);
  this->update(); //Obligatory: when the 'source/from' QtNode moves, this update causes the QtEdge keep pointing to
  //if (this->scene()) { this->scene()->update(); } // Not needed
}

void ribi::cmap::QtEdge1::OnHeadArrowChanged(Edge * const edge) noexcept
{
  SetHasHeadArrow(edge->HasHeadArrow());
  this->update(); //Obligatory
  m_signal_edge_changed(this);
}

/*
void ribi::cmap::QtEdge1::OnNodeChanged(QtNode * const node) noexcept
{
  m_qtnode->SetCenterX(edge->GetNode()->GetX());
  m_qtnode->SetCenterY(edge->GetNode()->GetY());
  m_qtnode->SetText( { edge->GetNode()->GetConcept()->GetName() } );
  this->update();
  m_signal_edge_changed(this);
}
*/

void ribi::cmap::QtEdge1::OnNodeChanged(Edge * const edge) noexcept
{
  m_qtnode->SetCenterX(edge->GetNode()->GetX());
  m_qtnode->SetCenterY(edge->GetNode()->GetY());
  m_qtnode->SetText( { edge->GetNode()->GetConcept()->GetName() } );
  this->update();
  if (this->scene()) { this->scene()->update(); }
  m_signal_edge_changed(this);
}

void ribi::cmap::QtEdge1::OnNodePosChanged(QtRoundedRectItem * const node) noexcept
{
  this->m_qtnode->SetCenterX(node->x());
  this->m_qtnode->SetCenterY(node->y());
  this->m_qtnode->update();
  this->update();
  m_signal_edge_changed(this);
}

void ribi::cmap::QtEdge1::OnTailArrowChanged(Edge * const edge) noexcept
{
  SetHasTailArrow(edge->HasTailArrow());
  assert(edge->HasTailArrow() == this->GetArrow()->HasTail());
  m_signal_edge_changed(this);
}

void ribi::cmap::QtEdge1::OnTextChanged(QtRoundedEditRectItem* item) noexcept
{
  const auto new_name = item->GetText()[0];
  const auto old_name = GetEdge()->GetNode()->GetConcept()->GetName();
  if (old_name != new_name)
  {
    this->GetEdge()->GetNode()->GetConcept()->SetName(new_name);
    m_signal_edge_changed(this);
  }
}

void ribi::cmap::QtEdge1::OnToChanged(Edge * const edge) noexcept
{
  this->GetTo()->SetNode(edge->GetTo());
  assert(edge->GetTo() == this->GetTo()->GetNode());
  m_signal_edge_changed(this);
  this->update(); //Obligatory: when the 'target/to' QtNode moves, this update causes the QtEdge keep pointing to
}


void ribi::cmap::QtEdge1::OnArrowChanged(const QtQuadBezierArrowItem* const item)
{
  GetEdge()->GetNode()->GetConcept()->SetName(
    Container().Concatenate(m_qtnode->GetText())
  );
  GetEdge()->SetHeadArrow(item->HasHead());
  GetEdge()->SetTailArrow(item->HasTail());
  this->update();

  //this->m_signal_item_has_updated(this);
}

void ribi::cmap::QtEdge1::OnMustUpdateScene()
{
  if (scene()) { scene()->update(); }
}

void ribi::cmap::QtEdge1::OnRequestSceneUpdate()
{
  if (scene()) { scene()->update(); }
  //this->m_signal_request_scene_update();
}


void ribi::cmap::QtEdge1::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) noexcept
{
  if (!this->m_arrow->scene())
  {
    assert(this->scene());
    this->scene()->addItem(m_arrow.get()); //Must be removed?

  }
  if (!this->m_qtnode->scene())
  {
    assert(this->scene());
    this->scene()->addItem(m_qtnode.get()); //Must be removed?

  }
  assert(this->scene());
  assert(this->m_arrow->scene());
  assert(this->m_qtnode->scene());
  assert(this->m_from->scene());

  if (m_arrow->isVisible())
  {
    //Translate
    //painter->translate(m_qtnode->GetCenterPos());

    //Paint
    m_arrow->paint(painter,option,widget);

    //Untranslate
    //painter->translate(-m_qtnode->GetCenterPos());
  }

  if (m_qtnode->isVisible())
  {
    //Translate
    painter->translate(m_qtnode->GetCenterPos());

    //Paint
    m_qtnode->paint(painter,option,widget);

    //Untranslate
    painter->translate(-m_qtnode->GetCenterPos());
  }

  const QPen pen{
    this->hasFocus() || this->isSelected()
    ? this->GetQtNode()->GetFocusPen()
    : this->GetQtNode()->GetContourPen()
  };
  m_arrow->SetPen(pen);
  m_qtnode->setPen(pen);

  #define FIXING_BUG_260
  #ifdef FIXING_BUG_260
  //if (show_bounding_rect)
  {
    const QPen prev_pen = painter->pen();
    const QBrush prev_brush = painter->brush();
    painter->setPen(QPen(QColor(96,0,0)));
    painter->setBrush(QBrush(QColor(255,0,0,32)));
    painter->drawRect(this->boundingRect().adjusted(1.0,1.0,-1.0,-1.0));
    painter->setPen(prev_pen);
    painter->setBrush(prev_brush);
  }
  #endif
}

void ribi::cmap::QtEdge1::SetEdge(const boost::shared_ptr<Edge>& edge) noexcept
{
  const bool verbose{false};

  if (m_edge == edge)
  {
    return;
  }

  if (verbose)
  {
    std::stringstream s;
    s << "Setting edge '" << edge->ToStr() << "'\n";
  }

  bool from_changed{true};
  bool has_head_changed{true};
  bool has_tail_changed{true};
  bool node_changed{true};
  bool to_changed{true};

  if (m_edge && edge)
  {
    const auto from_after = edge->GetFrom();
    const auto has_head_after = edge->HasHeadArrow();
    const auto has_tail_after = edge->HasTailArrow();
    const auto node_after = edge->GetNode();
    const auto to_after = edge->GetTo();

    const auto from_before = m_edge->GetFrom();
    const auto has_head_before = m_edge->HasHeadArrow();
    const auto has_tail_before = m_edge->HasTailArrow();
    const auto node_before = m_edge->GetNode();
    const auto to_before = m_edge->GetTo();

    from_changed = from_before != from_after;
    has_head_changed = has_head_before != has_head_after;
    has_tail_changed = has_tail_before != has_tail_after;
    node_changed = node_before != node_after;
    to_changed = to_before != to_after;


    if (verbose)
    {
      if (from_changed)
      {
        std::stringstream s;
        s
          << "From will change from "
          << from_before
          << " to "
          << from_after
          << '\n'
        ;
        TRACE(s.str());
      }
      if (has_head_changed)
      {
        std::stringstream s;
        s
          << "Has head will change from "
          << has_head_before
          << " to "
          << has_head_after
          << '\n'
        ;
        TRACE(s.str());
      }
      if (has_tail_changed)
      {
        std::stringstream s;
        s
          << "Has tail will change from "
          << has_tail_before
          << " to "
          << has_tail_after
          << '\n'
        ;
        TRACE(s.str());
      }
      if (node_changed)
      {
        std::stringstream s;
        s << "Node will change from " << node_before->ToStr()
          << " to " << node_after->ToStr() << '\n';
        TRACE(s.str());
      }
      if (to_changed)
      {
        std::stringstream s;
        s << "To will change from " << to_before
          << " to " << to_after << '\n';
        TRACE(s.str());
      }
    }
  }

  if (m_edge)
  {
    //Disconnect m_concept
    m_edge->m_signal_from_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdge1::OnFromChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_head_arrow_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdge1::OnHeadArrowChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_node_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdge1::OnNodeChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_tail_arrow_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdge1::OnTailArrowChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_to_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdge1::OnToChanged,this,boost::lambda::_1)
    );
  }

  //Replace
  m_edge = edge;

  if (!m_edge) { return; }

  //Sync
  m_qtnode->SetCenterX(m_edge->GetNode()->GetX());
  m_qtnode->SetCenterY(m_edge->GetNode()->GetY());
  m_qtnode->SetText( { m_edge->GetNode()->GetConcept()->GetName() } );

  m_edge->m_signal_from_changed.connect(
    boost::bind(&ribi::cmap::QtEdge1::OnFromChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_head_arrow_changed.connect(
    boost::bind(&ribi::cmap::QtEdge1::OnHeadArrowChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_node_changed.connect(
    boost::bind(&ribi::cmap::QtEdge1::OnNodeChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_tail_arrow_changed.connect(
    boost::bind(&ribi::cmap::QtEdge1::OnTailArrowChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_to_changed.connect(
    boost::bind(&ribi::cmap::QtEdge1::OnToChanged,this,boost::lambda::_1)
  );

  //Emit everything that has changed
  if (from_changed)
  {
    m_edge->m_signal_from_changed(m_edge.get());
  }
  if (has_head_changed)
  {
    m_edge->m_signal_head_arrow_changed(m_edge.get());
  }
  if (has_tail_changed)
  {
    m_edge->m_signal_tail_arrow_changed(m_edge.get());
  }
  if (node_changed)
  {
    m_edge->m_signal_node_changed(m_edge.get());
  }
  if (to_changed)
  {
    m_edge->m_signal_to_changed(m_edge.get());
  }

  assert( edge ==  m_edge);
  assert(*edge == *m_edge);
}

void ribi::cmap::QtEdge1::SetFrom(const From& from) noexcept
{
  m_from = from;
}

void ribi::cmap::QtEdge1::SetHasHeadArrow(const bool has_head_arrow) noexcept
{
  assert(m_arrow);
  assert(m_edge);
  this->m_edge->SetHeadArrow(has_head_arrow);
  this->m_arrow->SetHasHead(has_head_arrow);
}

void ribi::cmap::QtEdge1::SetHasTailArrow(const bool has_tail_arrow) noexcept
{
  assert(m_arrow);
  assert(m_edge);
  this->m_edge->SetTailArrow(has_tail_arrow);
  this->m_arrow->SetHasTail(has_tail_arrow);
}


void ribi::cmap::QtEdge1::SetTo(const To& to) noexcept
{
  m_to = to;
}

QPainterPath ribi::cmap::QtEdge1::shape() const noexcept
{
  return m_qtnode->shape().translated(m_qtnode->GetCenterPos())
    .united(m_arrow->shape())
  ;
}

std::string ribi::cmap::QtEdge1::ToStr() const noexcept
{
  std::stringstream s;
  s << (*this);
  return s.str();
}

std::ostream& ribi::cmap::operator<<(std::ostream& os, const QtEdge1& qtedge) noexcept
{
  os
    << (*qtedge.GetEdge())
  ;
  return os;
}

bool ribi::cmap::operator==(const QtEdge1& /*lhs*/, const QtEdge1& /*rhs*/) noexcept
{
  //A stub
  return true;
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedge2.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPEDGEITEM2_H
#define QTCONCEPTMAPEDGEITEM2_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <QGraphicsItem>
#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace ribi {

struct QtRoundedRectItem;

namespace cmap {

///The QtEdge2 is a QtConceptMapElement that
///draws a curve underneath itself, between head and tail arrowhead
///concept_item is the Strategy for displaying the ConceptItem
//struct QtEdge2 : public QtConceptMapElement
struct QtEdge2 : public QGraphicsItem
{
  using Base = QGraphicsItem;

  using Arrow = boost::shared_ptr<QtQuadBezierArrowItem>;
  using ReadOnlyArrow = boost::shared_ptr<const QtQuadBezierArrowItem> ;
  using NodePtr = boost::shared_ptr<Node>;
  using ReadOnlyNodePtr = boost::shared_ptr<const Node>;
  using QtNodePtr =  QtNode *;
  using ReadOnlyQtNodePtr = const QtNode *;
  using EdgePtr = boost::shared_ptr<Edge>;
  using ReadOnlyEdgePtr = boost::shared_ptr<const Edge>;

  using From = QtNodePtr;
  using ReadOnlyFrom = ReadOnlyQtNodePtr;
  using To = QtNodePtr;
  using ReadOnlyTo = ReadOnlyQtNodePtr;

  QtEdge2(
    const EdgePtr& edge,
    const From& from,
    const To& to
  );
  QtEdge2(const QtEdge2&) = delete;
  QtEdge2& operator=(const QtEdge2&) = delete;
  ~QtEdge2() noexcept;

  QRectF boundingRect() const override final;

  void DisableAll() noexcept;
  void EnableAll() noexcept;

  ReadOnlyArrow GetArrow() const noexcept { return m_arrow; }
  const Arrow& GetArrow() noexcept { return m_arrow; }

  ReadOnlyEdgePtr GetEdge() const noexcept { return m_edge; }
  EdgePtr GetEdge() noexcept { return m_edge; }

  ///The node item the arrow originates from
  ReadOnlyFrom GetFrom() const noexcept { return m_from; }
  From GetFrom() noexcept { return m_from; }

  ///The node item the arrow targets
  ReadOnlyTo GetTo() const noexcept { return m_to; }
  To GetTo() noexcept { return m_to; }

  boost::shared_ptr<      QtNode> GetQtNode()       noexcept { return m_qtnode; }
  boost::shared_ptr<const QtNode> GetQtNode() const noexcept { return m_qtnode; }

  static std::string GetVersion() noexcept;
  static std::vector<std::string> GetVersionHistory() noexcept;

  void SetEdge(const EdgePtr& edge) noexcept;

  void SetFrom(const From& from) noexcept;

  void SetHasHeadArrow(const bool has_head_arrow) noexcept;
  void SetHasTailArrow(const bool has_tail_arrow) noexcept;

  void SetTo(const To& to) noexcept;

  std::string ToStr() const noexcept;

  mutable boost::signals2::signal<void (QtEdge2 *)> m_signal_base_changed;
  mutable boost::signals2::signal<void (QtEdge2 *)> m_signal_edge_changed;
  mutable boost::signals2::signal<void (QtEdge2 *)> m_signal_focus_in_event;
  mutable boost::signals2::signal<void (QtEdge2 *,const int key)> m_signal_key_down_pressed;

protected:
  /*
  void dragEnterEvent(QGraphicsSceneDragDropEvent *) noexcept override final;
  void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) noexcept override final;
  void dragMoveEvent(QGraphicsSceneDragDropEvent *event) noexcept override final;
  */
  void focusInEvent(QFocusEvent *event) noexcept override final;
  /*
  void hoverMoveEvent(QGraphicsSceneHoverEvent *event) noexcept override final;
  */
  void keyPressEvent(QKeyEvent *event) noexcept override final;
  void mousePressEvent(QGraphicsSceneMouseEvent *event) noexcept override final;
  void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) noexcept override final;
  //QPainterPath shape() const noexcept override final;

private:
  ///The arrow used for drawing
  const Arrow m_arrow;

  ///The edge
  boost::shared_ptr<Edge> m_edge;

  ///The node item the arrow originates from
  From m_from;

  ///The QtNode around Edge its Node
  const boost::shared_ptr<QtNode> m_qtnode;

  ///The node item the arrow targets
  To m_to;

  ///Called whenever the edge changes
  void OnEdgeChanged(Edge * const edge) noexcept;
  void OnConceptChanged(Node * const node) noexcept;
  void OnFromChanged(Edge * const edge) noexcept;
  void OnHeadArrowChanged(Edge * const edge) noexcept;
  void OnNodeChanged(Edge * const edge) noexcept;
  void OnNodePosChanged(QtRoundedRectItem * const node) noexcept;
  void OnTailArrowChanged(Edge * const edge) noexcept;
  void OnTextChanged(QtRoundedEditRectItem* item) noexcept;
  void OnToChanged(Edge * const edge) noexcept;

  ///Called whenever the arrow updates
  void OnArrowChanged(const QtQuadBezierArrowItem* const item);

  void OnMustUpdateScene();
  void OnRequestSceneUpdate();

  #ifndef NDEBUG
  //static void Test() noexcept;
  #endif
};

std::ostream& operator<<(std::ostream& os, const QtEdge2& qtedge) noexcept;

bool operator==(const QtEdge2& lhs, const QtEdge2& rhs) noexcept;

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPEDGEITEM2_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedge2.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapqtedge2.h"

#include <cassert>

#include <boost/lambda/lambda.hpp>

#include <QCursor>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QKeyEvent>
#include <QPainter>

#include "conceptmapconcept.h"
#include "conceptmapedge.h"
#include "conceptmapnode.h"
#include "container.h"
#include "qtconceptmapqtnode.h"
#include "qtquadbezierarrowitem.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::cmap::QtEdge2::QtEdge2(
    const EdgePtr& edge,
    const From& from,
    const To& to
)
  : m_signal_base_changed{},
    m_signal_edge_changed{},
    m_signal_focus_in_event{},
    m_signal_key_down_pressed{},
    //m_arrow{new QtQuadBezierArrowItem(from,edge->HasTailArrow(),this,edge->HasHeadArrow(),to)},
    m_arrow{nullptr}, //Will be initialized below
    m_edge{}, //Will be initialized by setEdge
    m_from{from},
    m_qtnode{new QtNode(edge->GetNode())},
    m_to{to}
{
  #ifndef NDEBUG
  //Test();
  #endif

  //Allow mouse tracking
  //OTOH: must be done by the other thing
  //this->setAcceptHoverEvents(true);

  //const_cast because Arrow constant
  //I just need to have an initialized m_qtnode
  const_cast<Arrow&>(m_arrow).reset(
    new QtQuadBezierArrowItem(
    from,edge->HasTailArrow(),
    this->GetQtNode().get(),
    edge->HasHeadArrow(),
    to
    )
  );

  //QtEdge2 is just the glue between a collection of things
  /*
  this->setFlags(
      QGraphicsItem::ItemIsFocusable
    | QGraphicsItem::ItemIsMovable
    | QGraphicsItem::ItemIsSelectable
  );
  */

  GetQtNode()->setFlags(
      QGraphicsItem::ItemIsFocusable
    | QGraphicsItem::ItemIsMovable
    | QGraphicsItem::ItemIsSelectable
  );

  GetFrom()->setFlags(
      QGraphicsItem::ItemIsFocusable
    | QGraphicsItem::ItemIsMovable
    | QGraphicsItem::ItemIsSelectable
  );

  GetTo()->setFlags(
      QGraphicsItem::ItemIsFocusable
    | QGraphicsItem::ItemIsMovable
    | QGraphicsItem::ItemIsSelectable
  );

  assert(m_from);
  assert(m_to);
  assert(from != to);
  assert(m_from != m_to);
  //m_edge must be initialized before m_arrow
  //if 'from' or 'to' are CenterNodes, then no item must be put at the center
  //const bool is_connected_to_center_node
  //  = dynamic_cast<QtCenterNode*>(from) || dynamic_cast<QtCenterNode*>(to);
  //if (is_connected_to_center_node)
  //{
  //m_arrow.reset(new QtQuadBezierArrowItem(from,edge->HasTailArrow(),this,edge->HasHeadArrow(),to));
  //}
  //else
  //{
  //  m_arrow.reset(new QtQuadBezierArrowItem(from,edge->HasTailArrow(),this,edge->HasHeadArrow(),to));
  //}
  assert(m_arrow);




  m_arrow->m_signal_item_updated.connect(
    boost::bind(&ribi::cmap::QtEdge2::OnArrowChanged,this,boost::lambda::_1)
  );

  m_qtnode->m_signal_pos_changed.connect(
    boost::bind(&ribi::cmap::QtEdge2::OnNodePosChanged,this,boost::lambda::_1)
  );

  //TODO: Is this a redundant connection? (i.e. a subset of what m_signal_node_changed covers)
  m_qtnode->m_signal_text_changed.connect(
    boost::bind(&ribi::cmap::QtEdge2::OnTextChanged,this,boost::lambda::_1)
  );

  m_from->m_signal_node_changed.connect(
    boost::bind(&ribi::cmap::QtEdge2::OnMustUpdateScene,this)
  );
  m_to->m_signal_node_changed.connect(
    boost::bind(&ribi::cmap::QtEdge2::OnMustUpdateScene,this)
  );

  SetEdge(edge);


  #ifdef TODO_ISSUE_212
  if (!is_connected_to_center_node)
  {
    //Only allow edges not connected to the center node to be edited
    assert(display_strategy);
    QtEditStrategy * const edit_concept = dynamic_cast<QtEditStrategy*>(display_strategy.get());
    if (edit_concept)
    {
      edit_concept->m_signal_request_edit.connect(
        boost::bind(
          &QtConceptMapElement::OnConceptRequestsEdit,
          this
        )
      );
    }
  }
  #else
  //if (QtEditStrategy * edit_concept = dynamic_cast<QtEditStrategy*>(display_strategy.get()))
  //{
  //  edit_concept->m_signal_request_edit.connect(
  //    boost::bind(
  //      &QtConceptMapElement::OnConceptRequestsEdit,
  //      this
  //    )
  //  );
  //}
  #endif
}

ribi::cmap::QtEdge2::~QtEdge2() noexcept
{
  m_arrow->m_signal_item_updated.disconnect(
    boost::bind(&ribi::cmap::QtEdge2::OnArrowChanged,this,boost::lambda::_1)
  );
  m_qtnode->m_signal_text_changed.disconnect(
    boost::bind(&ribi::cmap::QtEdge2::OnTextChanged,this,boost::lambda::_1)
  );

  SetEdge(nullptr);

  //assert(m_from);
  //assert(m_from->m_signal_node_changed.num_slots() > 0);

  //m_from->m_signal_node_changed.disconnect(
  //  boost::bind(&ribi::cmap::QtEdge2::OnMustUpdateScene,this)
  //);
  //assert(m_to);
  //m_to->m_signal_node_changed.disconnect(
  //  boost::bind(&ribi::cmap::QtEdge2::OnMustUpdateScene,this)
  //);


  //Disconnect signals
  /*
  #ifdef TODO_ISSUE_212
  if (!is_connected_to_center_node)
  {
    //Only allow edges not connected to the center node to be edited
    assert(m_display_strategy);
    QtEditStrategy * const edit_concept = dynamic_cast<QtEditStrategy*>(m_display_strategy.get());
    if (edit_concept)
    {
      edit_concept->m_signal_request_edit.disconnect(
        boost::bind(&QtConceptMapElement::OnConceptRequestsEdit,this)
      );
    }
  }
  #else
  if (QtEditStrategy * edit_concept = dynamic_cast<QtEditStrategy*>(display_strategy.get()))
  {
    edit_concept->m_signal_request_edit.disconnect(
      boost::bind(
        &QtConceptMapElement::OnConceptRequestsEdit,
        this
      )
    );
  }
  #endif
  */
}

QRectF ribi::cmap::QtEdge2::boundingRect() const
{
  //Goes around arrow
  //return m_qtnode->boundingRect().translated(-m_qtnode->GetCenterPos())
  //  .united(m_arrow->boundingRect());

  //Goes around arrow
  return m_qtnode->boundingRect()
    .united(m_arrow->boundingRect());

  //return m_qtnode->boundingRect()
  //  .united(m_arrow->boundingRect().translated(-m_qtnode->GetCenterPos()));
}

void ribi::cmap::QtEdge2::DisableAll() noexcept
{
  this->setEnabled(false);
  this->setVisible(false);
  this->m_arrow->setEnabled(false);
  this->m_arrow->setVisible(false);
}

void ribi::cmap::QtEdge2::EnableAll() noexcept
{
  this->setEnabled(true);
  this->setVisible(true);
  this->m_arrow->setEnabled(true);
  this->m_arrow->setVisible(true);
}

/*
boost::shared_ptr<const ribi::cmap::Node> ribi::cmap::QtEdge2::GetNode() const noexcept
{
  const auto p = m_edge->GetNode();
  assert(p);
  return p;
}

boost::shared_ptr<ribi::cmap::Node> ribi::cmap::QtEdge2::GetNode() noexcept
{
  const auto p = m_edge->GetNode();
  assert(p);
  return p;
}
*/
std::string ribi::cmap::QtEdge2::GetVersion() noexcept
{
  return "1.1";
}

std::vector<std::string> ribi::cmap::QtEdge2::GetVersionHistory() noexcept
{
  return {
    "201x-xx-xx: version 1.0: initial version"
    "2014-08-01: version 1.1: start of versioning"
  };
}

/*
void ribi::cmap::QtEdge2::dragEnterEvent(QGraphicsSceneDragDropEvent *) noexcept
{
  TRACE_FUNC();
  update();
}

void ribi::cmap::QtEdge2::dragLeaveEvent(QGraphicsSceneDragDropEvent *) noexcept
{
  TRACE_FUNC();
  update();
}

void ribi::cmap::QtEdge2::dragMoveEvent(QGraphicsSceneDragDropEvent *) noexcept
{
  TRACE_FUNC();
  //if (scene()) { scene()->update(); }
  update();
}
*/

void ribi::cmap::QtEdge2::focusInEvent(QFocusEvent*) noexcept
{
  m_signal_focus_in_event(this);
  //Lose focus of arrow
  //m_arrow->SetPen(QPen(QColor(0,0,0)));
  //m_display_strategy->SetContourPen(m_display_strategy->GetFocusPen()); //Updates itself
}
/*
void ribi::cmap::QtEdge2::focusOutEvent(QFocusEvent*) noexcept
{
  m_arrow->SetPen(QPen(QColor(0,0,0)));
  //m_display_strategy->SetContourPen(m_display_strategy->GetContourPen()); //Updates itself
}
*/

/*
void ribi::cmap::QtEdge2::hoverMoveEvent(QGraphicsSceneHoverEvent*) noexcept
{
  this->setCursor(QCursor(Qt::PointingHandCursor));
}
*/

void ribi::cmap::QtEdge2::keyPressEvent(QKeyEvent *event) noexcept
{
  assert(m_arrow);
  assert(m_edge);
  m_signal_key_down_pressed(this,event->key());
  /*
  if (m_arrow->isEnabled())
  {
    m_arrow->keyPressEvent(event);
    m_edge->SetHeadArrow( m_arrow->HasHead() );
    m_edge->SetTailArrow( m_arrow->HasTail() );

    assert(m_edge->HasHeadArrow() == m_arrow->HasHead());
    assert(m_edge->HasTailArrow() == m_arrow->HasTail());
  }
  */
  QGraphicsItem::keyPressEvent(event);
}

void ribi::cmap::QtEdge2::mousePressEvent(QGraphicsSceneMouseEvent *event) noexcept
{  
  assert( m_arrow->HasTail() == m_edge->HasTailArrow() );
  assert( m_arrow->HasHead() == m_edge->HasHeadArrow() );
  if (event->modifiers() & Qt::ShiftModifier)
  {
    if ((event->pos() - this->m_arrow->GetTail() + m_qtnode->GetCenterPos()).manhattanLength() < 20.0)
    {
      this->SetHasTailArrow( !m_arrow->HasTail() ); //FIX 2013-02-10
      //this->m_arrow->SetHasTail( !m_arrow->HasTail() ); //BUG 2013-02-10
      //this->update(); //Don't!
      //m_signal_item_updated(this); //Don't!
    }
    else if ((event->pos() - this->m_arrow->GetHead() + m_qtnode->GetCenterPos()).manhattanLength() < 20.0)
    {
      this->SetHasHeadArrow( !m_arrow->HasHead() );
      //this->update(); //Don't!
      //m_signal_item_updated(this); //Don't!
    }
  }

  //What is clicked on: the concept or the arrow? Assume concept
  m_arrow->SetPen(QPen(QColor(0,0,0)));
  if (!m_qtnode->GetInnerRect().contains(event->pos()))
  {
    //If the concept is not clicked...
    //but the arrow is...
    QPointF pos_on_arrow = event->pos();
    pos_on_arrow += (m_qtnode->GetCenterPos());
    if (m_arrow->shape().contains(pos_on_arrow)
      || (event->pos() - this->m_arrow->GetTail() + m_qtnode->GetCenterPos()).manhattanLength() < 20.0
      || (event->pos() - this->m_arrow->GetHead() + m_qtnode->GetCenterPos()).manhattanLength() < 20.0
      )
    {
      //give focus to the arrow
      m_arrow->SetPen(m_arrow->GetFocusPen());
      return;
    }
  }
  //QtConceptMapElement::mousePressEvent(event);
}

void ribi::cmap::QtEdge2::OnConceptChanged(Node * const node) noexcept
{
  //Node changed, sync QtRoundedRectItem
  assert(node);
  assert(node == m_edge->GetNode().get());
  const std::string new_str{node->GetConcept()->GetName()};
  const std::vector<std::string> new_text{new_str};
  assert(new_text.size() == 1);
  m_qtnode->SetText(new_text);
  assert(m_qtnode->GetText() == new_text);
}

void ribi::cmap::QtEdge2::OnEdgeChanged(Edge * const edge) noexcept
{
  OnFromChanged(edge);
  OnNodeChanged(edge);
  OnHeadArrowChanged(edge);
  OnTailArrowChanged(edge);
  OnToChanged(edge);
}


void ribi::cmap::QtEdge2::OnFromChanged(Edge * const edge) noexcept
{
  this->GetFrom()->SetNode(edge->GetFrom());
  assert(edge->GetFrom() == this->GetFrom()->GetNode());
  m_signal_edge_changed(this);
  this->update(); //Obligatory: when the 'source/from' QtNode moves, this update causes the QtEdge2 keep pointing to
  //if (this->scene()) { this->scene()->update(); } // Not needed
}

void ribi::cmap::QtEdge2::OnHeadArrowChanged(Edge * const edge) noexcept
{
  SetHasHeadArrow(edge->HasHeadArrow());
  this->update(); //Obligatory
  m_signal_edge_changed(this);
}

/*
void ribi::cmap::QtEdge2::OnNodeChanged(QtNode * const node) noexcept
{
  m_qtnode->SetCenterX(edge->GetNode()->GetX());
  m_qtnode->SetCenterY(edge->GetNode()->GetY());
  m_qtnode->SetText( { edge->GetNode()->GetConcept()->GetName() } );
  this->update();
  m_signal_edge_changed(this);
}
*/

void ribi::cmap::QtEdge2::OnNodeChanged(Edge * const edge) noexcept
{
  m_qtnode->SetCenterX(edge->GetNode()->GetX());
  m_qtnode->SetCenterY(edge->GetNode()->GetY());
  m_qtnode->SetText( { edge->GetNode()->GetConcept()->GetName() } );
  this->update();
  m_signal_edge_changed(this);
}

void ribi::cmap::QtEdge2::OnNodePosChanged(QtRoundedRectItem * const node) noexcept
{
  this->m_qtnode->SetCenterX(node->x());
  this->m_qtnode->SetCenterY(node->y());
  this->m_qtnode->update();
  this->update();
  m_signal_edge_changed(this);
}

void ribi::cmap::QtEdge2::OnTailArrowChanged(Edge * const edge) noexcept
{
  SetHasTailArrow(edge->HasTailArrow());
  assert(edge->HasTailArrow() == this->GetArrow()->HasTail());
  m_signal_edge_changed(this);
}

void ribi::cmap::QtEdge2::OnTextChanged(QtRoundedEditRectItem* item) noexcept
{
  const auto new_name = item->GetText()[0];
  const auto old_name = GetEdge()->GetNode()->GetConcept()->GetName();
  if (old_name != new_name)
  {
    this->GetEdge()->GetNode()->GetConcept()->SetName(new_name);
    m_signal_edge_changed(this);
  }
}

void ribi::cmap::QtEdge2::OnToChanged(Edge * const edge) noexcept
{
  this->GetTo()->SetNode(edge->GetTo());
  assert(edge->GetTo() == this->GetTo()->GetNode());
  m_signal_edge_changed(this);
  this->update(); //Obligatory: when the 'target/to' QtNode moves, this update causes the QtEdge2 keep pointing to
}


void ribi::cmap::QtEdge2::OnArrowChanged(const QtQuadBezierArrowItem* const item)
{
  GetEdge()->GetNode()->GetConcept()->SetName(
    Container().Concatenate(m_qtnode->GetText())
  );
  GetEdge()->SetHeadArrow(item->HasHead());
  GetEdge()->SetTailArrow(item->HasTail());
  this->update();

  //this->m_signal_item_has_updated(this);
}

void ribi::cmap::QtEdge2::OnMustUpdateScene()
{
  if (scene()) { scene()->update(); }
}

void ribi::cmap::QtEdge2::OnRequestSceneUpdate()
{
  if (scene()) { scene()->update(); }
  //this->m_signal_request_scene_update();
}


void ribi::cmap::QtEdge2::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) noexcept
{
  if (m_arrow->isVisible())
  {
    //Translate
    //painter->translate(m_qtnode->GetCenterPos());

    //Paint
    m_arrow->paint(painter,option,widget);

    //Untranslate
    //painter->translate(-m_qtnode->GetCenterPos());
  }

  if (m_qtnode->isVisible())
  {
    //Translate
    painter->translate(m_qtnode->GetCenterPos());

    //Paint
    m_qtnode->paint(painter,option,widget);

    //Untranslate
    painter->translate(-m_qtnode->GetCenterPos());
  }

  const QPen pen{
    this->hasFocus() || this->isSelected()
    ? this->GetQtNode()->GetFocusPen()
    : this->GetQtNode()->GetContourPen()
  };
  m_arrow->SetPen(pen);
  m_qtnode->setPen(pen);

  #define FIXING_BUG_260
  #ifdef FIXING_BUG_260
  //if (show_bounding_rect)
  {
    const QPen prev_pen = painter->pen();
    const QBrush prev_brush = painter->brush();
    painter->setPen(QPen(QColor(0,0,96)));
    painter->setBrush(QBrush(QColor(0,0,255,64)));
    painter->drawRect(this->boundingRect().adjusted(1.0,1.0,-1.0,-1.0));
    painter->setPen(prev_pen);
    painter->setBrush(prev_brush);
  }
  #endif
}

void ribi::cmap::QtEdge2::SetEdge(const boost::shared_ptr<Edge>& edge) noexcept
{
  const bool verbose{false};

  if (m_edge == edge)
  {
    return;
  }

  if (verbose)
  {
    std::stringstream s;
    s << "Setting edge '" << edge->ToStr() << "'\n";
  }

  bool from_changed{true};
  bool has_head_changed{true};
  bool has_tail_changed{true};
  bool node_changed{true};
  bool to_changed{true};

  if (m_edge && edge)
  {
    const auto from_after = edge->GetFrom();
    const auto has_head_after = edge->HasHeadArrow();
    const auto has_tail_after = edge->HasTailArrow();
    const auto node_after = edge->GetNode();
    const auto to_after = edge->GetTo();

    const auto from_before = m_edge->GetFrom();
    const auto has_head_before = m_edge->HasHeadArrow();
    const auto has_tail_before = m_edge->HasTailArrow();
    const auto node_before = m_edge->GetNode();
    const auto to_before = m_edge->GetTo();

    from_changed = from_before != from_after;
    has_head_changed = has_head_before != has_head_after;
    has_tail_changed = has_tail_before != has_tail_after;
    node_changed = node_before != node_after;
    to_changed = to_before != to_after;


    if (verbose)
    {
      if (from_changed)
      {
        std::stringstream s;
        s
          << "From will change from "
          << from_before
          << " to "
          << from_after
          << '\n'
        ;
        TRACE(s.str());
      }
      if (has_head_changed)
      {
        std::stringstream s;
        s
          << "Has head will change from "
          << has_head_before
          << " to "
          << has_head_after
          << '\n'
        ;
        TRACE(s.str());
      }
      if (has_tail_changed)
      {
        std::stringstream s;
        s
          << "Has tail will change from "
          << has_tail_before
          << " to "
          << has_tail_after
          << '\n'
        ;
        TRACE(s.str());
      }
      if (node_changed)
      {
        std::stringstream s;
        s << "Node will change from " << node_before->ToStr()
          << " to " << node_after->ToStr() << '\n';
        TRACE(s.str());
      }
      if (to_changed)
      {
        std::stringstream s;
        s << "To will change from " << to_before
          << " to " << to_after << '\n';
        TRACE(s.str());
      }
    }
  }

  if (m_edge)
  {
    //Disconnect m_concept
    m_edge->m_signal_from_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdge2::OnFromChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_head_arrow_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdge2::OnHeadArrowChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_node_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdge2::OnNodeChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_tail_arrow_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdge2::OnTailArrowChanged,this,boost::lambda::_1)
    );
    m_edge->m_signal_to_changed.disconnect(
      boost::bind(&ribi::cmap::QtEdge2::OnToChanged,this,boost::lambda::_1)
    );
  }

  //Replace
  m_edge = edge;

  if (!m_edge) { return; }

  //Sync
  m_qtnode->SetCenterX(m_edge->GetNode()->GetX());
  m_qtnode->SetCenterY(m_edge->GetNode()->GetY());
  m_qtnode->SetText( { m_edge->GetNode()->GetConcept()->GetName() } );

  m_edge->m_signal_from_changed.connect(
    boost::bind(&ribi::cmap::QtEdge2::OnFromChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_head_arrow_changed.connect(
    boost::bind(&ribi::cmap::QtEdge2::OnHeadArrowChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_node_changed.connect(
    boost::bind(&ribi::cmap::QtEdge2::OnNodeChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_tail_arrow_changed.connect(
    boost::bind(&ribi::cmap::QtEdge2::OnTailArrowChanged,this,boost::lambda::_1)
  );
  m_edge->m_signal_to_changed.connect(
    boost::bind(&ribi::cmap::QtEdge2::OnToChanged,this,boost::lambda::_1)
  );

  //Emit everything that has changed
  if (from_changed)
  {
    m_edge->m_signal_from_changed(m_edge.get());
  }
  if (has_head_changed)
  {
    m_edge->m_signal_head_arrow_changed(m_edge.get());
  }
  if (has_tail_changed)
  {
    m_edge->m_signal_tail_arrow_changed(m_edge.get());
  }
  if (node_changed)
  {
    m_edge->m_signal_node_changed(m_edge.get());
  }
  if (to_changed)
  {
    m_edge->m_signal_to_changed(m_edge.get());
  }

  assert( edge ==  m_edge);
  assert(*edge == *m_edge);
}

void ribi::cmap::QtEdge2::SetFrom(const From& from) noexcept
{
  m_from = from;
}

void ribi::cmap::QtEdge2::SetHasHeadArrow(const bool has_head_arrow) noexcept
{
  assert(m_arrow);
  assert(m_edge);
  this->m_edge->SetHeadArrow(has_head_arrow);
  this->m_arrow->SetHasHead(has_head_arrow);
}

void ribi::cmap::QtEdge2::SetHasTailArrow(const bool has_tail_arrow) noexcept
{
  assert(m_arrow);
  assert(m_edge);
  this->m_edge->SetTailArrow(has_tail_arrow);
  this->m_arrow->SetHasTail(has_tail_arrow);
}


void ribi::cmap::QtEdge2::SetTo(const To& to) noexcept
{
  m_to = to;
}

/*
QPainterPath ribi::cmap::QtEdge2::shape() const noexcept
{
  return
    m_qtnode->shape()
    .united(m_arrow->shape().translated(-m_qtnode->GetCenterPos()));
}
*/

std::string ribi::cmap::QtEdge2::ToStr() const noexcept
{
  std::stringstream s;
  s << (*this);
  return s.str();
}

std::ostream& ribi::cmap::operator<<(std::ostream& os, const QtEdge2& qtedge) noexcept
{
  os
    << (*qtedge.GetEdge())
  ;
  return os;
}

bool ribi::cmap::operator==(const QtEdge2& /*lhs*/, const QtEdge2& /*rhs*/) noexcept
{
  //A stub
  return true;
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedge_test.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapqtedge.h"

#include <cassert>

#include <boost/lambda/lambda.hpp>

#include "conceptmapconcept.h"
#include "conceptmapedgefactory.h"
#include "conceptmapedge.h"
#include "conceptmapnodefactory.h"
#include "conceptmapnode.h"
#include "counter.h"
#include "qtconceptmapqtnode.h"
#include "qtconceptmapqtedgefactory.h"
#include "qtquadbezierarrowitem.h"
#include "qtroundededitrectitem.h"
#include "testtimer.h"
#include "trace.h"
#pragma GCC diagnostic pop

#ifndef NDEBUG
void ribi::cmap::QtEdge::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  {
    NodeFactory();
    QtRoundedEditRectItem();
    QtRoundedEditRectItem a;
    QtRoundedEditRectItem b;
    QtQuadBezierArrowItem(&a,false,nullptr,true,&b);
    const boost::shared_ptr<Node> node_from{NodeFactory().GetTest(0)};
    const boost::shared_ptr<Node> node_to{NodeFactory().GetTest(0)};
    const boost::shared_ptr<QtNode> qtnode_from{new QtNode(node_from)};
    const boost::shared_ptr<QtNode> qtnode_to{new QtNode(node_to)};
    const boost::shared_ptr<Edge> edge{EdgeFactory().GetTest(0,node_from,node_to)};
    //const boost::shared_ptr<QtEdge> qtedge{QtEdgeFactory().Create(edge,qtnode_from,qtnode_to)};
    const boost::shared_ptr<QtEdge> qtedge{QtEdgeFactory().Create(edge,qtnode_from.get(),qtnode_to.get())};
  }

  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  const int node_test_index{0};
  const int edge_test_index{0};
  const boost::shared_ptr<Node> node_from{NodeFactory().GetTest(node_test_index)};
  const boost::shared_ptr<Node> node_to{NodeFactory().GetTest(node_test_index)};
  const boost::shared_ptr<QtNode> qtnode_from{new QtNode(node_from)};
  const boost::shared_ptr<QtNode> qtnode_to{new QtNode(node_to)};
  const boost::shared_ptr<Edge> edge{EdgeFactory().GetTest(edge_test_index,node_from,node_to)};
  const boost::shared_ptr<QtEdge> qtedge{new QtEdge(edge,qtnode_from.get(),qtnode_to.get())};
  //const boost::shared_ptr<QtEdge> qtedge{new QtEdge(edge,qtnode_from,qtnode_to)};
  const boost::shared_ptr<QtRoundedEditRectItem> qtitem{boost::dynamic_pointer_cast<QtRoundedEditRectItem>(qtedge->GetQtNode())};
  assert(qtitem);

  if (verbose) { TRACE("QtEdge must accept hover events"); } //NOT SURE
  {
    //assert(qtedge->acceptHoverEvents()); //Must remove the 's' in Qt5?
  }
  if (verbose) { TRACE("QtEdge its arrow must accept hover events"); }
  {
    assert(qtedge->GetArrow()->acceptHoverEvents()); //Must remove the 's' in Qt5?
  }
  //Head arrow
  if (verbose) { TRACE("An Edge's head arrow and it QtQuadBezierArrowItem must match at creation"); }
  {
    const bool edge_has_head{qtedge->GetEdge()->HasHeadArrow()};
    const bool arrow_has_head{qtedge->GetArrow()->HasHead()};
    assert(edge_has_head == arrow_has_head);
  }
  if (verbose) { TRACE("If a QtEdge its Edge's head arrow is changed, a signal must be emitted by Edge"); }
  {
    Counter c{0}; //For receiving the signal
    qtedge->GetEdge()->m_signal_head_arrow_changed.connect(
      boost::bind(&ribi::Counter::Inc,&c) //Do not forget the &
    );
    qtedge->GetEdge()->SetHeadArrow(true);
    qtedge->GetEdge()->SetHeadArrow(false);
    assert(c.Get() > 0);
  }
  if (verbose) { TRACE("If a QtEdge its Edge's head arrow is changed, a signal must be emitted by QtEdge"); }
  {
    Counter c{0}; //For receiving the signal
    qtedge->m_signal_edge_changed.connect(
      boost::bind(&ribi::Counter::Inc,&c) //Do not forget the &
    );
    qtedge->GetEdge()->SetHeadArrow(false);
    qtedge->GetEdge()->SetHeadArrow(true);
    assert(c.Get() > 0);
  }
  if (verbose) { TRACE("If a QtEdge its Edge's head arrow is changed, it QtQuadBezier must match"); }
  {
    qtedge->GetEdge()->SetHeadArrow(false);
    assert(!qtedge->GetArrow()->HasHead());
    qtedge->GetEdge()->SetHeadArrow(true);
    assert(qtedge->GetArrow()->HasHead());
  }
  //Tail arrow
  if (verbose) { TRACE("An Edge's tail arrow and it QtQuadBezierArrowItem must match at creation"); }
  {
    const bool edge_has_tail{qtedge->GetEdge()->HasTailArrow()};
    const bool arrow_has_tail{qtedge->GetArrow()->HasTail()};
    assert(edge_has_tail == arrow_has_tail);
  }
  if (verbose) { TRACE("If a QtEdge its Edge's tail arrow is changed, a signal must be emitted by Edge"); }
  {
    Counter c{0}; //For receiving the signal
    qtedge->GetEdge()->m_signal_tail_arrow_changed.connect(
      boost::bind(&ribi::Counter::Inc,&c) //Do not forget the &
    );
    qtedge->GetEdge()->SetTailArrow(true);
    qtedge->GetEdge()->SetTailArrow(false);
    assert(c.Get() > 0);
  }
  if (verbose) { TRACE("If a QtEdge its Edge's tail arrow is changed, a signal must be emitted by QtEdge"); }
  {
    Counter c{0}; //For receiving the signal
    qtedge->m_signal_edge_changed.connect(
      boost::bind(&ribi::Counter::Inc,&c) //Do not forget the &
    );
    qtedge->GetEdge()->SetTailArrow(false);
    qtedge->GetEdge()->SetTailArrow(true);
    assert(c.Get() > 0);
  }
  if (verbose) { TRACE("If a QtEdge its Edge's tail arrow is changed, it QtQuadBezier must match"); }
  {
    qtedge->GetEdge()->SetTailArrow(false);
    assert(!qtedge->GetArrow()->HasTail());
    qtedge->GetEdge()->SetTailArrow(true);
    assert(qtedge->GetArrow()->HasTail());
  }
  //Text
  if (verbose) { TRACE("Text of QtEdge must be one line"); }
  {
    assert(qtitem->GetText().size() == 1);
  }
  if (verbose) { TRACE("Text of QtEdge and QtRoundedEditRectItem must match at creation"); }
  {
    const std::string qtitem_name{qtitem->GetText()[0]};
    const std::string qtedge_name{qtedge->GetEdge()->GetNode()->GetConcept()->GetName()};
    assert(qtitem_name == qtedge_name);
  }
  if (verbose) { TRACE("If a QtEdge its text is changed, a signal must be emitted"); }
  {
    qtedge->GetEdge()->GetNode()->GetConcept()->SetName("A");
    Counter c{0}; //For receiving the signal
    qtedge->m_signal_edge_changed.connect(
      boost::bind(&ribi::Counter::Inc,&c) //Do not forget the &
    );
    qtedge->GetEdge()->GetNode()->GetConcept()->SetName("B");
    assert(c.Get()>0);
  }
  if (verbose) { TRACE("If text is set via QtEdge, QtRoundedEditRectItem must sync"); }
  {
    qtedge->GetEdge()->GetNode()->GetConcept()->SetName("A");
    assert(qtitem->GetText()[0] == "A");
    qtedge->GetEdge()->GetNode()->GetConcept()->SetName("B");
    assert(qtitem->GetText()[0] == "B");
  }
  if (verbose) { TRACE("If text is set via QtRoundedEditRectItem, QtEdge must sync"); }
  {
    qtitem->SetText( { "A" } );
    assert(qtedge->GetEdge()->GetNode()->GetConcept()->GetName() == "A");
    qtitem->SetText( { "B" } );
    assert(qtedge->GetEdge()->GetNode()->GetConcept()->GetName() == "B");
  }

  //X
  if (verbose) { TRACE("X of QtEdge and QtRoundedEditRectItem must match at creation"); }
  {
    const double edge_x{edge->GetNode()->GetX()};
    const double qtedge_x{qtitem->GetCenterX()};
    assert(edge_x == qtedge_x);
  }
  if (verbose) { TRACE("If X is set via QtEdge, QtRoundedEditRectItem must sync"); }
  {
    const double old_x{qtedge->m_qtnode->GetCenterX()};
    const double new_x{old_x + 10.0};
    qtedge->m_qtnode->SetCenterX(new_x);
    assert(std::abs(qtitem->GetCenterX() - new_x) < 2.0);
  }
  if (verbose) { TRACE("If X is set via QtRoundedEditRectItem, QtEdge must sync"); }
  {
    const double old_x{qtitem->GetCenterX()};
    const double new_x{old_x + 10.0};
    qtitem->SetCenterX(new_x);
    assert(std::abs(qtedge->m_qtnode->GetCenterX() - new_x) < 2.0);
  }
  //Y
  if (verbose) { TRACE("Y of QtEdge and QtRoundedEditRectItem must match at creation"); }
  {
    const double edge_y{edge->GetNode()->GetY()};
    const double qtedge_y{qtitem->GetCenterY()};
    assert(edge_y == qtedge_y);
  }
  if (verbose) { TRACE("If Y is set via QtEdge, QtRoundedEditRectItem must sync"); }
  {
    const double old_y{qtedge->m_qtnode->GetCenterY()};
    const double new_y{old_y + 10.0};
    qtedge->m_qtnode->SetCenterY(new_y);
    assert(std::abs(qtitem->GetCenterY() - new_y) < 2.0);
  }
  if (verbose) { TRACE("If Y is set via QtRoundedEditRectItem, QtEdge must sync"); }
  {
    const double old_y{qtitem->GetCenterY()};
    const double new_y{old_y + 10.0};
    qtitem->SetCenterY(new_y);
    assert(std::abs(qtedge->m_qtnode->GetCenterY() - new_y) < 2.0);
  }
  //From
  if (verbose) { TRACE("If qtnode_from is moved, a signal must be emitted by -at least- QtEdge"); }
  {
    Counter c{0}; //For receiving the signal
    qtedge->GetEdge()->m_signal_from_changed.connect(
      boost::bind(&ribi::Counter::Inc,&c) //Do not forget the &
    );
    qtnode_from->SetCenterX(qtnode_from->GetCenterX() + 10.0);
    assert(c.Get() > 0);
  }
  if (verbose) { TRACE("If qtnode_from is moved, a signal must be emitted by QtEdge"); }
  {
    Counter c{0}; //For receiving the signal
    qtedge->m_signal_edge_changed.connect(
      boost::bind(&ribi::Counter::Inc,&c) //Do not forget the &
    );
    qtnode_from->SetCenterX(qtnode_from->GetCenterX() + 10.0);
    assert(c.Get() > 0);
  }
  //To
  if (verbose) { TRACE("If qtnode_to is moved, a signal must be emitted by -at least- Edge"); }
  {
    Counter c{0}; //For receiving the signal
    qtedge->GetEdge()->m_signal_to_changed.connect(
      boost::bind(&ribi::Counter::Inc,&c) //Do not forget the &
    );
    qtnode_to->SetCenterX(qtnode_to->GetCenterX() + 10.0);
    assert(c.Get() > 0);
  }
  if (verbose) { TRACE("If qtnode_to is moved, a signal must be emitted by QtEdge"); }
  {
    Counter c{0}; //For receiving the signal
    qtedge->m_signal_edge_changed.connect(
      boost::bind(&ribi::Counter::Inc,&c) //Do not forget the &
    );
    qtnode_to->SetCenterX(qtnode_to->GetCenterX() + 10.0);
    assert(c.Get() > 0);
  }

  //Center
  if (verbose) { TRACE("If a QtEdge its center node is changed, a signal must be emitted by Edge"); }
  {
    Counter c{0}; //For receiving the signal

    qtedge->m_signal_edge_changed.connect(
      boost::bind(&ribi::Counter::Inc,&c) //Do not forget the &
    );
    qtedge->GetQtNode()->SetCenterX( qtedge->GetQtNode()->GetCenterX() + 123.45);
    assert(c.Get() > 0);
  }

  {
    const double new_x{qtedge->GetQtNode()->GetCenterX() + 123.45};
    qtedge->GetQtNode()->SetCenterX(new_x);
    assert(qtedge->GetQtNode()->GetCenterX() == new_x);
  }
  {
    const double new_y{qtedge->GetQtNode()->GetCenterY() + 123.45};
    qtedge->GetQtNode()->SetCenterY(new_y);
    assert(qtedge->GetQtNode()->GetCenterY() == new_y);
  }
  assert(qtedge->GetArrow()->GetMidItem() == qtedge->GetQtNode().get());
  //assert(!"Red phase done");

}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedgedialog.h

 

#ifndef QTCONCEPTMAPQTEDGEDIALOG_H
#define QTCONCEPTMAPQTEDGEDIALOG_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"
#include "qtconceptmapqtedge.h"
#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop


namespace Ui {
  class QtQtEdgeDialog;
}

namespace ribi {
namespace cmap {

class QtQtEdgeDialog : public ::ribi::QtHideAndShowDialog
{
  Q_OBJECT

public:
  explicit QtQtEdgeDialog(QWidget *parent = 0);
  QtQtEdgeDialog(const QtQtEdgeDialog&) = delete;
  QtQtEdgeDialog& operator=(const QtQtEdgeDialog&) = delete;
  ~QtQtEdgeDialog();


  static int GetMinimumHeight(const QtEdge& qtedge) noexcept;
  boost::shared_ptr<QtEdge> GetQtEdge() const noexcept { return m_qtedge; }

  ///Get if there is an arrow at the head of the edge directly from the GUI
  bool GetUiHasHeadArrow() const noexcept;
  ///Get if there is an arrow at the tail of the edge directly from the GUI
  bool GetUiHasTailArrow() const noexcept;
  //Obtain the X coordinat of the Node on the Edge from the GUI
  double GetUiX() const noexcept;
  //Obtain the Y coordinat of the Node on the Edge from the GUI
  double GetUiY() const noexcept;

  void SetQtEdge(const boost::shared_ptr<QtEdge>& qtedge) noexcept;

  ///Set if there is an arrow at the head of the edge directly to the GUI
  void SetUiHasHeadArrow(const bool has_head) noexcept;
  ///Set if there is an arrow at the tail of the edge directly to the GUI
  void SetUiHasTailArrow(const bool has_tail) noexcept;
  //Set the X coordinat of the Node on the Edge via the GUI
  void SetUiX(const double x) const noexcept;
  //Set the Y coordinat of the Node on the Edge via the GUI
  void SetUiY(const double y) const noexcept;

private:
  Ui::QtQtEdgeDialog *ui;

  ///The QtEdge to work on
  ///A QtEdge is
  /// (1) a Edge
  /// (2) an Arrow
  /// (3) inherited properties from QtRoundedEditRectItem
  boost::shared_ptr<QtEdge> m_qtedge;

  ///QtDialog that displays (1) a Edge
  boost::shared_ptr<QtEdgeDialog> m_qtedgedialog;

  ///QtDialog that displays (2) an Arrow

  ///QtDialog that displays (3) inherited properties from QtRoundedEditRectItem
  boost::shared_ptr<QtRoundedEditRectItemDialog> m_qtroundededitrectitem_dialog;

  //void OnDisplayStrategyChanged(QtEdge * const qtedge) noexcept;
  void OnEdgeChanged(const QtEdge * const qtedge) noexcept;
  void OnQtRoundedRectItemChanged(QtEdge * const qtedge) noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPQTEDGEDIALOG_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedgedialog.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapqtedgedialog.h"

#include <cassert>
#include <sstream>

#include <boost/lambda/lambda.hpp>

#include <QDesktopWidget>
#include <QVBoxLayout>

#include "conceptmapedgefactory.h"
#include "conceptmapedge.h"
#include "conceptmapnodefactory.h"
#include "qtconceptmapedgedialog.h"
#include "qtconceptmapqtedge.h"
#include "qtconceptmapqtnode.h"
#include "qtconceptmapqtnodefactory.h"
#include "qtquadbezierarrowitem.h"
#include "qtitemdisplaystrategy.h"
#include "qtroundededitrectitemdialog.h"
#include "qtroundededitrectitem.h"
#include "testtimer.h"
#include "trace.h"
#include "ui_qtconceptmapqtedgedialog.h"
#pragma GCC diagnostic pop

ribi::cmap::QtQtEdgeDialog::QtQtEdgeDialog(QWidget *parent)
  : QtHideAndShowDialog(parent),
  ui(new Ui::QtQtEdgeDialog),
  m_qtedge{},
  m_qtedgedialog{new QtEdgeDialog},
  m_qtroundededitrectitem_dialog{new QtRoundedEditRectItemDialog}
{
  #ifndef NDEBUG
  Test();
  #endif
  ui->setupUi(this);

  {
    assert(!this->ui->groupBox->layout());
    //assert(!this->layout());
    QVBoxLayout * const my_layout{new QVBoxLayout};
    this->ui->groupBox->setLayout(my_layout);
    //this->setLayout(my_layout);
    my_layout->addWidget(m_qtedgedialog.get());
    my_layout->addWidget(m_qtroundededitrectitem_dialog.get());
  }

  {
    //Put the dialog in the screen center
    const QRect screen = QApplication::desktop()->screenGeometry();
    this->setGeometry(0,0,screen.width() * 9 / 10, screen.height() * 9 / 10);
    this->move( screen.center() - this->rect().center() );
  }
}

ribi::cmap::QtQtEdgeDialog::~QtQtEdgeDialog()
{
  this->layout()->removeWidget(m_qtedgedialog.get());
  this->layout()->removeWidget(m_qtroundededitrectitem_dialog.get());
  this->SetQtEdge(nullptr);
  delete ui;
}

int ribi::cmap::QtQtEdgeDialog::GetMinimumHeight(const QtEdge& qtedge) noexcept
{
  const int margin = 16;
  return
    QtEdgeDialog::GetMinimumHeight(*qtedge.GetEdge())
  + margin
  + QtRoundedEditRectItemDialog::GetMinimumHeight()
  ;
}

bool ribi::cmap::QtQtEdgeDialog::GetUiHasHeadArrow() const noexcept
{
  return this->m_qtedgedialog->GetUiHasHeadArrow();
}

bool ribi::cmap::QtQtEdgeDialog::GetUiHasTailArrow() const noexcept
{
  return this->m_qtedgedialog->GetUiHasTailArrow();
}

double ribi::cmap::QtQtEdgeDialog::GetUiX() const noexcept
{
  return this->m_qtedgedialog->GetUiX();
}

double ribi::cmap::QtQtEdgeDialog::GetUiY() const noexcept
{
  return this->m_qtedgedialog->GetUiY();
}

void ribi::cmap::QtQtEdgeDialog::OnEdgeChanged(const QtEdge * const
#ifndef NDEBUG
  qtedge
#endif // NDEBUG
) noexcept
{
  assert( qtedge ==  m_qtedge.get());
  assert(*qtedge == *m_qtedge);
  const boost::shared_ptr<QtRoundedEditRectItem> item{
    boost::dynamic_pointer_cast<QtRoundedEditRectItem>(m_qtedge->GetQtNode())
  };
  assert(item);
  m_qtroundededitrectitem_dialog->SetItem(item);

}

void ribi::cmap::QtQtEdgeDialog::OnQtRoundedRectItemChanged(QtEdge * const qtedge) noexcept
{
  m_qtedgedialog->SetEdge(qtedge->GetEdge());
}

void ribi::cmap::QtQtEdgeDialog::SetQtEdge(const boost::shared_ptr<QtEdge>& qtedge) noexcept
{
  const bool verbose{false};

  if (m_qtedge == qtedge)
  {
    return;
  }

  if (verbose)
  {
    std::stringstream s;
    s << "Setting edge '" << qtedge->ToStr() << "'\n";
  }


  bool qtroundededitrectitem_changed{true};
  bool edge_changed{true};

  if (m_qtedge && qtedge)
  {
    const auto qtroundededitrectitem_after = qtedge.get();
    const auto edge_after = qtedge->GetEdge();

    const auto qtroundededitrectitem_before = m_qtedge.get();
    const auto edge_before = m_qtedge->GetEdge();

    qtroundededitrectitem_changed = qtroundededitrectitem_before != qtroundededitrectitem_after;
    edge_changed = edge_before != edge_after;


    if (verbose)
    {
      if (qtroundededitrectitem_changed)
      {
        std::stringstream s;
        s
          << "DisplayStrategy will change from "
          << qtroundededitrectitem_before->ToStr()
          << " to "
          << qtroundededitrectitem_after->ToStr()
          << '\n'
        ;
        TRACE(s.str());
      }
      if (edge_changed)
      {
        std::stringstream s;
        s << "QtEdge will change from " << (*edge_before)
          << " to " << (*edge_after) << '\n';
        TRACE(s.str());
      }
    }
  }

  if (m_qtedge)
  {
    //Disconnect old
    m_qtedge->m_signal_base_changed.disconnect(
      boost::bind(&ribi::cmap::QtQtEdgeDialog::OnQtRoundedRectItemChanged,this,boost::lambda::_1)
    );
    m_qtedge->m_signal_edge_changed.disconnect(
      boost::bind(&ribi::cmap::QtQtEdgeDialog::OnEdgeChanged,this,boost::lambda::_1)
    );
  }

  //Replace by the new
  m_qtedge = qtedge;

  if (!m_qtedge) return;

  m_qtedge->m_signal_base_changed.connect(
    boost::bind(&ribi::cmap::QtQtEdgeDialog::OnQtRoundedRectItemChanged,this,boost::lambda::_1)
  );
  m_qtedge->m_signal_edge_changed.connect(
    boost::bind(&ribi::cmap::QtQtEdgeDialog::OnEdgeChanged,this,boost::lambda::_1)
  );

  //Emit everything that has changed
  if (qtroundededitrectitem_changed)
  {
    m_qtedge->m_signal_base_changed(m_qtedge.get());
  }
  if (edge_changed)
  {
    m_qtedge->m_signal_edge_changed(m_qtedge.get());
  }

  this->setMinimumHeight(
    this->GetMinimumHeight(
      *m_qtedge
    )
  );

  assert( qtedge ==  m_qtedge);
  assert(*qtedge == *m_qtedge);
}

void ribi::cmap::QtQtEdgeDialog::SetUiHasHeadArrow(const bool has_head) noexcept
{
  this->m_qtedgedialog->SetUiHasHeadArrow(has_head);
}

void ribi::cmap::QtQtEdgeDialog::SetUiHasTailArrow(const bool has_tail) noexcept
{
  this->m_qtedgedialog->SetUiHasTailArrow(has_tail);
}

void ribi::cmap::QtQtEdgeDialog::SetUiX(const double x) const noexcept
{
  this->m_qtedgedialog->SetUiX(x);
}

void ribi::cmap::QtQtEdgeDialog::SetUiY(const double y) const noexcept
{
  this->m_qtedgedialog->SetUiY(y);
}

#ifndef NDEBUG
void ribi::cmap::QtQtEdgeDialog::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  {
    NodeFactory();
    QtEdgeDialog();
    QtNodeFactory();
    QtRoundedEditRectItemDialog();
    QtRoundedEditRectItem();
    const auto from = NodeFactory().GetTest(1);
    const auto to = NodeFactory().GetTest(1);
    const auto qtfrom = QtNodeFactory().Create(from);
    const auto qtto = QtNodeFactory().Create(to);
    const auto edge = EdgeFactory().GetTest(1,from,to);
    //QtEdge(edge,qtfrom.get(),qtto.get());
    QtEdge(edge,qtfrom.get(),qtto.get());
  }

  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};

  if (verbose) { TRACE("Constructor"); }
  {
    QtQtEdgeDialog();
  }
  QtQtEdgeDialog dialog;
  const auto from = NodeFactory().GetTest(1);
  const auto to = NodeFactory().GetTest(1);
  const auto edge = EdgeFactory().GetTest(1,from,to);
  const auto qtfrom = QtNodeFactory().Create(from);
  const auto qtto = QtNodeFactory().Create(to);
  const boost::shared_ptr<QtEdge> qtedge(new QtEdge(edge,qtfrom.get(),qtto.get()));
  //const boost::shared_ptr<QtEdge> qtedge(new QtEdge(edge,qtfrom,qtto));
  dialog.SetQtEdge(qtedge);

  if (verbose) { TRACE("SetX and GetX must be symmetric"); }
  {
    const double new_x{dialog.GetUiX() + 10.0};
    dialog.SetUiX(new_x);
    assert(std::abs(dialog.GetUiX() - new_x) < 2.0);
  }
  if (verbose) { TRACE("SetY and GetY must be symmetric"); }
  {
    const double new_y{dialog.GetUiY() + 10.0};
    dialog.SetUiY(new_y);
    assert(std::abs(dialog.GetUiY() - new_y) < 2.0);
  }
  //X
  if (verbose) { TRACE("X of QtQtEdgeDialog and QtEdge must match at creation"); }
  {
    const double ui_x{dialog.GetUiX()};
    const double qtedge_x{qtedge->GetQtNode()->GetCenterX()};
    assert(std::abs(ui_x - qtedge_x) < 2.0);
  }
  if (verbose) { TRACE("If X is set via QtQtEdgeDialog, QtEdge must sync"); }
  {
    const double old_x{dialog.GetUiX()};
    const double new_x{old_x + 10.0};
    dialog.SetUiX(new_x);
    assert(std::abs(new_x - qtedge->GetQtNode()->GetCenterX()) < 2.0);
  }
  if (verbose) { TRACE("If X is set via QtEdge, QtQtEdgeDialog must sync"); }
  {
    const double old_x{dialog.GetUiX()};
    const double new_x{old_x + 10.0};
    qtedge->GetQtNode()->SetCenterX(new_x);
    assert(std::abs(new_x - dialog.GetUiX()) < 2.0);
  }

  //HasHeadArrow
  if (verbose) { TRACE("HasHeadArrow of QtQtEdgeDialog and QtEdge its Edge and QtEdge its Arrow must match at creation"); }
  {
    assert(dialog.GetUiHasHeadArrow() == qtedge->GetEdge()->HasHeadArrow());
    assert(dialog.GetUiHasHeadArrow() == qtedge->GetArrow()->HasHead());
  }
  if (verbose) { TRACE("If HasHeadArrow is set via QtQtEdgeDialog, QtEdge its Edge and QtEdge its Arrow must sync"); }
  {
    dialog.SetUiHasHeadArrow(!dialog.GetUiHasHeadArrow());
    assert(dialog.GetUiHasHeadArrow() == qtedge->GetEdge()->HasHeadArrow());
    assert(dialog.GetUiHasHeadArrow() == qtedge->GetArrow()->HasHead());
  }
  if (verbose) { TRACE("If HasHeadArrow is set via QtEdge its Edge, QtEdge its Arrow and QtQtEdgeDialog must sync"); }
  {
    qtedge->GetEdge()->SetHeadArrow(!qtedge->GetEdge()->HasHeadArrow());
    assert(dialog.GetUiHasHeadArrow() == qtedge->GetEdge()->HasHeadArrow());
    assert(dialog.GetUiHasHeadArrow() == qtedge->GetArrow()->HasHead());
  }
  if (verbose) { TRACE("If HasHeadArrow is set via QtEdge its Arrow, QtEdge its Edge and QtQtEdgeDialog must sync"); }
  {
    qtedge->GetArrow()->SetHasHead(!qtedge->GetArrow()->HasHead());
    assert(dialog.GetUiHasHeadArrow() == qtedge->GetEdge()->HasHeadArrow());
    assert(dialog.GetUiHasHeadArrow() == qtedge->GetArrow()->HasHead());
  }
  //HasTailArrow
  if (verbose) { TRACE("HasTailArrow of QtQtEdgeDialog and QtEdge its Edge and QtEdge its Arrow must match at creation"); }
  {
    assert(dialog.GetUiHasTailArrow() == qtedge->GetEdge()->HasTailArrow());
    assert(dialog.GetUiHasTailArrow() == qtedge->GetArrow()->HasTail());
  }
  if (verbose) { TRACE("If HasTailArrow is set via QtQtEdgeDialog, QtEdge its Edge and QtEdge its Arrow must sync"); }
  {
    dialog.SetUiHasTailArrow(!dialog.GetUiHasTailArrow());
    assert(dialog.GetUiHasTailArrow() == qtedge->GetEdge()->HasTailArrow());
    assert(dialog.GetUiHasTailArrow() == qtedge->GetArrow()->HasTail());
  }
  if (verbose) { TRACE("If HasTailArrow is set via QtEdge its Edge, QtEdge its Arrow and QtQtEdgeDialog must sync"); }
  {
    qtedge->GetEdge()->SetTailArrow(!qtedge->GetEdge()->HasTailArrow());
    assert(dialog.GetUiHasTailArrow() == qtedge->GetEdge()->HasTailArrow());
    assert(dialog.GetUiHasTailArrow() == qtedge->GetArrow()->HasTail());
  }
  if (verbose) { TRACE("If HasTailArrow is set via QtEdge its Arrow, QtEdge its Edge and QtQtEdgeDialog must sync"); }
  {
    qtedge->GetArrow()->SetHasTail(!qtedge->GetArrow()->HasTail());
    assert(dialog.GetUiHasTailArrow() == qtedge->GetEdge()->HasTailArrow());
    assert(dialog.GetUiHasTailArrow() == qtedge->GetArrow()->HasTail());
  }
  dialog.SetQtEdge(nullptr);
  //dialog = QtQtEdgeDialog();
}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedgefactory.h

 

#ifndef QTCONCEPTMAPQTEDGEFACTORY_H
#define QTCONCEPTMAPQTEDGEFACTORY_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"
#include "qtconceptmapqtedge.h"

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

struct QtEdgeFactory
{
  QtEdgeFactory();

  boost::shared_ptr<QtEdge> Create(
    const boost::shared_ptr<Edge>& node,
    QtNode* const from,
    //const boost::shared_ptr<QtNode>& from,
    QtNode* const to
    //const boost::shared_ptr<QtNode>& to
  ) const noexcept;


  ///Obtain a Edge or CenterEdge from an XML std::string
  boost::shared_ptr<Edge> FromXml(const std::string& s) const noexcept;

  int GetNumberOfTests() const noexcept;

  std::vector<boost::shared_ptr<QtEdge>> GetTests(
    const boost::shared_ptr<QtNode>& from,
    const boost::shared_ptr<QtNode>& to
  ) const noexcept;

  boost::shared_ptr<QtEdge> GetTest(
    const int test,
    const boost::shared_ptr<QtNode>& from,
    const boost::shared_ptr<QtNode>& to
  ) const noexcept;

  private:
  #ifndef NDEBUG
  void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPQTEDGEFACTORY_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtedgefactory.cpp

 

#include "qtconceptmapqtedgefactory.h"

#include <cassert>

#include "conceptmapedgefactory.h"
#include "conceptmapedge.h"
#include "conceptmapnode.h"
#include "qtconceptmapqtedge.h"
#include "qtconceptmapqtnode.h"
#include "qtconceptmapqtnodefactory.h"
#include "testtimer.h"
#include "trace.h"

ribi::cmap::QtEdgeFactory::QtEdgeFactory()
{
  #ifndef NDEBUG
  Test();
  #endif
}

boost::shared_ptr<ribi::cmap::QtEdge> ribi::cmap::QtEdgeFactory::Create(
  const boost::shared_ptr<Edge>& edge,
  QtNode* const from,
  QtNode* const to
) const noexcept
{
  assert(edge);
  boost::shared_ptr<QtEdge> qtedge{new QtEdge(edge,from,to)};
  assert(qtedge);
  return qtedge;
}

int ribi::cmap::QtEdgeFactory::GetNumberOfTests() const noexcept
{
  return EdgeFactory().GetNumberOfTests();
}

boost::shared_ptr<ribi::cmap::QtEdge> ribi::cmap::QtEdgeFactory::GetTest(
  const int i,
  const boost::shared_ptr<QtNode>& from,
  const boost::shared_ptr<QtNode>& to
) const noexcept
{
  const auto tests = GetTests(from,to);
  assert(i >= 0);
  #ifndef NDEBUG
  if(i >= static_cast<int>(tests.size()))
  {
    TRACE("BREAK");
  }
  #endif
  assert(i < static_cast<int>(tests.size()));
  return tests[i];
}

std::vector<boost::shared_ptr<ribi::cmap::QtEdge>> ribi::cmap::QtEdgeFactory::GetTests(
  const boost::shared_ptr<QtNode>& from,
  const boost::shared_ptr<QtNode>& to
) const noexcept
{
  std::vector<boost::shared_ptr<QtEdge>> qtedges;
  const auto v = EdgeFactory().GetTests(from->GetNode(),to->GetNode());
  std::transform(v.begin(),v.end(),std::back_inserter(qtedges),
    [from,to](const boost::shared_ptr<Edge>& c)
    {
      const boost::shared_ptr<QtEdge> q{QtEdgeFactory().Create(c,from.get(),to.get())};
      //const boost::shared_ptr<QtEdge> q{QtEdgeFactory().Create(c,from,to)};
      assert(q);
      q->GetEdge()->GetNode()->SetX((from->GetCenterX() + to->GetCenterX()) / 2.0);
      q->GetEdge()->GetNode()->SetY((from->GetCenterY() + to->GetCenterY()) / 2.0);
      return q;
    }
  );
  assert(GetNumberOfTests() == static_cast<int>(qtedges.size()));
  return qtedges;
}

#ifndef NDEBUG
void ribi::cmap::QtEdgeFactory::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  QtNodeFactory();
  QtNodeFactory().GetTest(0);
  QtEdgeFactory().GetTest(0,QtNodeFactory().GetTest(0),QtNodeFactory().GetTest(0));
  const TestTimer test_timer(__func__,__FILE__,1.0);
}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtnode.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPNODEITEM_H
#define QTCONCEPTMAPNODEITEM_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qtroundededitrectitem.h"
//#include "qtconceptmapelement.h"

#include "conceptmapfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

///QtNode displays a Node as a QtConceptMapElement
struct QtNode : public QtRoundedEditRectItem
{
  typedef QtRoundedEditRectItem Base;
  virtual ~QtNode() noexcept;

  ///Node cannot be const as it contains a Concept that the user might want to edit
  ///concept_item contains the Stategy to display (and respond to the concept)
  explicit QtNode(
    const boost::shared_ptr<Node> node
    //const boost::shared_ptr<QtItemDisplayStrategy> concept_item
  );

  virtual QRectF boundingRect() const { return Base::boundingRect(); }

  //QBrush brush() const;

  void DisableAll();
  void EnableAll();

  boost::shared_ptr<const Node>  GetNode() const noexcept;
  boost::shared_ptr<      Node>  GetNode()       noexcept;

  //boost::shared_ptr<const QtItemDisplayStrategy> GetDisplayStrategy() const noexcept final { return m_display_strategy; }
  //boost::shared_ptr<      QtItemDisplayStrategy> GetDisplayStrategy()       noexcept final { return m_display_strategy; }

  //      boost::shared_ptr<const cmap::Node>  GetNode() const noexcept { return m_node; }
  //const boost::shared_ptr<      cmap::Node>& GetNode()       noexcept { return m_node; }

  void SetNode(const boost::shared_ptr<Node>& node) noexcept;
  //void SetDisplay(const boost::shared_ptr<QtItemDisplayStrategy>& display_strategy);

  //void SetName(const std::string& name) noexcept;

  ///Set the X coordinat of the central concept
  //void SetX(const double x) noexcept;

  ///Set the Y coordinat of the central concept
  //void SetY(const double y) noexcept;

  std::string ToStr() const noexcept;

  ///m_signal_request_rate_node is emitted due to a m_signal_request_rate_node
  ///of the Node its QtRateConceptItem
  //boost::signals2::signal<void (QtNode *)> m_signal_display_changed;
  mutable boost::signals2::signal<void (QtNode *)> m_signal_base_changed;
  mutable boost::signals2::signal<void (QtNode *)> m_signal_focus_in_event;
  mutable boost::signals2::signal<void (QtNode *,const int key)> m_signal_key_down_pressed;
  mutable boost::signals2::signal<void (QtNode *)> m_signal_node_changed;
  //boost::signals2::signal<void (QtNode *)> m_signal_node_requests_rate_concept;
  //boost::signals2::signal<void (QtNode *)> m_signal_node_requests_rate_examples;
  ///No other signals, these are present in the ConceptItems

  virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem *, QWidget *) noexcept final;

protected:

  void focusInEvent(QFocusEvent *event) noexcept final override;
  void focusOutEvent(QFocusEvent *event) noexcept final override;
  void keyPressEvent(QKeyEvent *event) noexcept final;
  void hoverMoveEvent(QGraphicsSceneHoverEvent *event) noexcept final;

  //const boost::shared_ptr<QtConceptMapItem>& GetConceptItem() { return m_concept_item; }



private:

  ///The Strategy for displaying a Concept
  //boost::shared_ptr<QtItemDisplayStrategy> m_display_strategy;

  //const QPen m_contour_pen;
  //const QPen m_focus_pen;

  ///The node being edited, or displayed and not changed, or rated
  boost::shared_ptr<Node> m_node;

  bool m_show_bounding_rect;

  void OnItemHasUpdated();
  void OnPosChanged(const QtRoundedRectItem * const item) noexcept;
  void OnTextChanged(const QtRoundedRectItem * const item) noexcept;
  void OnXchanged(Node * const node) noexcept;
  void OnYchanged(Node * const node) noexcept;
  void OnConceptChanged(Node * const node) noexcept;

  ///This QtNode its Node changed
  //void OnRequestsSceneUpdate();

  ///The item
  //void OnItemRequestsRateConcept();
  //void OnItemRequestsRateExamples();

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

  //friend std::ostream& operator<<(std::ostream& os, const QtNode& qtnode) noexcept;
};

std::ostream& operator<<(std::ostream& os, const QtNode& qtnode) noexcept;

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPNODEITEM_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtnode.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapqtnode.h"

#include <cassert>
#include <climits>

#include <boost/lambda/lambda.hpp>
#include <boost/signals2.hpp>

#include <QCursor>
#include <QKeyEvent>
#include <QPainter>
#include <QPen>

#include "conceptmapconcept.h"
#include "conceptmapexamples.h"
#include "conceptmapnodefactory.h"
#include "conceptmapnode.h"
#include "container.h"
#include "qtconceptmapbrushfactory.h"
#include "qtconceptmapeditstrategy.h"
#include "qtconceptmapqtnodefactory.h"
#include "qtconceptmapratestrategy.h"
#include "qtitemdisplaystrategy.h"
#include "testtimer.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::cmap::QtNode::QtNode(
  const boost::shared_ptr<Node> node
)
  : QtRoundedEditRectItem(),
    //m_signal_display_changed{},
    m_signal_base_changed{},
    m_signal_focus_in_event{},
    m_signal_key_down_pressed{},
    m_signal_node_changed{},
    //m_signal_node_requests_rate_concept{},
    //m_signal_node_requests_rate_examples{},
    //m_contour_pen(concept_item->GetContourPen()),
    //m_focus_pen(concept_item->GetFocusPen()),
    m_node{},
    m_show_bounding_rect{true}
{
  #ifndef NDEBUG
  Test();
  #endif
  assert(node);

  //Allow mouse tracking
  this->setAcceptHoverEvents(true);

  this->SetPadding(Base::Padding(1.0,6.0,1.0,2.0));

  this->setAcceptHoverEvents(true);
  this->setFlags(
      QGraphicsItem::ItemIsFocusable
    | QGraphicsItem::ItemIsMovable
    | QGraphicsItem::ItemIsSelectable
  );


  SetNode(node);

  m_signal_pos_changed.connect(
    boost::bind(&ribi::cmap::QtNode::OnPosChanged,this,boost::lambda::_1)
  );

  this->m_signal_text_changed.connect(
    boost::bind(&ribi::cmap::QtNode::OnTextChanged,this,boost::lambda::_1)
  );

  /*

  m_display_strategy->m_signal_item_has_updated.connect(
    boost::bind(
      &ribi::cmap::QtNode::OnItemHasUpdated,this)
  );

  m_display_strategy->m_signal_request_scene_update.connect(
    boost::bind(
      &ribi::cmap::QtNode::OnRequestsSceneUpdate,
      this
    )
  );

  if (QtEditStrategy * edit_concept = dynamic_cast<QtEditStrategy*>(m_display_strategy.get()))
  {
    edit_concept->m_signal_request_edit.connect(
      boost::bind(
        &QtConceptMapElement::OnConceptRequestsEdit,
        this
      )
    );
  }

  if (QtRateStrategy * rate_concept = dynamic_cast<QtRateStrategy*>(m_display_strategy.get()))
  {
    rate_concept->m_signal_request_rate_concept.connect(
      boost::bind(
        &ribi::cmap::QtNode::OnItemRequestsRateConcept,
        this
      )
    );
    rate_concept->m_signal_request_rate_examples.connect(
      boost::bind(
        &ribi::cmap::QtNode::OnItemRequestsRateExamples,
        this
      )
    );
  }
  */
}

ribi::cmap::QtNode::~QtNode() noexcept
{
  /*
  m_display_strategy->m_signal_position_changed.disconnect(
    boost::bind(&ribi::cmap::QtNode::SetPos,this,boost::lambda::_1,boost::lambda::_2)
  );

  m_node->m_signal_concept_changed.disconnect(
    boost::bind(&ribi::cmap::QtNode::OnNodeChanged,this,boost::lambda::_1)
  );
  m_node->m_signal_x_changed.disconnect(
    boost::bind(&ribi::cmap::QtNode::OnXchanged,this,boost::lambda::_1)
  );
  m_node->m_signal_y_changed.disconnect(
    boost::bind(&ribi::cmap::QtNode::OnYchanged,this,boost::lambda::_1)
  );

  m_display_strategy->m_signal_item_has_updated.disconnect(
    boost::bind(
      &ribi::cmap::QtNode::OnItemHasUpdated,this)
  );

  m_display_strategy->m_signal_request_scene_update.disconnect(
    boost::bind(
      &ribi::cmap::QtNode::OnRequestsSceneUpdate,
      this
    )
  );

  if (QtEditStrategy * edit_concept = dynamic_cast<QtEditStrategy*>(m_display_strategy.get()))
  {
    edit_concept->m_signal_request_edit.disconnect(
      boost::bind(
        &QtConceptMapElement::OnConceptRequestsEdit,
        this
      )
    );
  }

  if (QtRateStrategy * rate_concept = dynamic_cast<QtRateStrategy*>(m_display_strategy.get()))
  {
    rate_concept->m_signal_request_rate_concept.disconnect(
      boost::bind(
        &ribi::cmap::QtNode::OnItemRequestsRateConcept,
        this
      )
    );
    rate_concept->m_signal_request_rate_examples.disconnect(
      boost::bind(
        &ribi::cmap::QtNode::OnItemRequestsRateExamples,
        this
      )
    );
  }
  */
}

void ribi::cmap::QtNode::DisableAll()
{
  this->setEnabled(false);
  this->setVisible(false);
}

void ribi::cmap::QtNode::EnableAll()
{
  this->setEnabled(true);
  this->setVisible(true);
}

void ribi::cmap::QtNode::focusInEvent(QFocusEvent* e) noexcept
{
  QtRoundedEditRectItem::focusInEvent(e);
  m_signal_focus_in_event(this);
  //m_display_strategy->SetContourPen(m_display_strategy->GetFocusPen()); //Updates itself
  //assert(!m_display_strategy->hasFocus());
  ///?maybe update?
  assert(hasFocus());
}

void ribi::cmap::QtNode::focusOutEvent(QFocusEvent* e) noexcept
{
  QtRoundedEditRectItem::focusOutEvent(e);
  //m_display_strategy->SetContourPen(m_display_strategy->GetContourPen()); //Updates itself
  //m_signal_item_has_updated(0); //causes Examples to get hidden
  ///?maybe update?
  assert(!hasFocus());
}

boost::shared_ptr<const ribi::cmap::Node> ribi::cmap::QtNode::GetNode() const noexcept
{
  const auto p = m_node;
  assert(p);
  return p;
}

boost::shared_ptr<ribi::cmap::Node> ribi::cmap::QtNode::GetNode() noexcept
{
  const auto p = m_node;
  assert(p);
  return p;
}

void ribi::cmap::QtNode::hoverMoveEvent(QGraphicsSceneHoverEvent*) noexcept
{
  this->setCursor(QCursor(Qt::PointingHandCursor));
  //m_concept_item->hoverMoveEvent(e);
  //Won't cause a hover, because the concept item
  //is not visible??
}


void ribi::cmap::QtNode::keyPressEvent(QKeyEvent *event) noexcept
{
  m_signal_key_down_pressed(this,event->key());
  Base::keyPressEvent(event);
}

void ribi::cmap::QtNode::OnItemHasUpdated()
{
  //this->setRect(m_display_strategy->boundingRect());

  //Cannot check here, as setRect triggers this member function
  //assert(m_concept_item->boundingRect() == QtConceptMapItem::boundingRect()
  //  && "Bounding rects must by synced");
  this->update();
  //this->m_signal_item_has_updated(this);
}

/*
void ribi::cmap::QtNode::OnItemRequestsRateConcept()
{
  m_signal_node_requests_rate_concept(this);
}

void ribi::cmap::QtNode::OnItemRequestsRateExamples()
{
  m_signal_node_requests_rate_examples(this);
}
*/

void ribi::cmap::QtNode::OnConceptChanged(Node * const node) noexcept
{
  //Node changed, sync QtRoundedRectItem
  assert(node);
  assert(node == m_node.get());
  const std::string new_str = node->GetConcept()->GetName();
  const std::vector<std::string> new_text{new_str};
  assert(new_text.size() == 1);
  this->SetText(new_text);
  assert(GetText() == new_text);
}

void ribi::cmap::QtNode::OnPosChanged(const QtRoundedRectItem * const item) noexcept
{
  //QtRoundedRectItem changed, sync Node
  assert(item);
  const auto new_pos = item->GetCenterPos();
  m_node->SetPos(new_pos.x(),new_pos.y());
}

void ribi::cmap::QtNode::OnTextChanged(const QtRoundedRectItem * const
#ifndef NDEBUG
  item
#endif
) noexcept
{
  //QtRoundedRectItem changed, sync Node
  assert(item);
  assert(item == this);
  const std::vector<std::string> new_text{GetText()};
  assert((new_text.size() == 1 || new_text.size() != 1)
    && "new_text can be of any number of lines, as QtRoundedEditRect supports this"
  );
  const std::string s{Container().Concatenate(new_text)};
  assert(std::count(std::begin(s),std::end(s),'\n') ==  0 && "Keep it single-line");
  m_node->GetConcept()->SetName(s);
}


void ribi::cmap::QtNode::OnXchanged(Node * const node) noexcept
{
  const bool verbose{false};
  if (verbose) { TRACE("Slot ribi::cmap::QtNode::OnXchanged"); }

  //Node changed, sync QtRoundedRectItem
  assert(node);
  SetCenterX(node->GetX());
}

void ribi::cmap::QtNode::OnYchanged(Node * const node) noexcept
{
  //Node changed, sync QtRoundedRectItem
  assert(node);
  SetCenterY(node->GetY());
}


//void ribi::cmap::QtNode::OnRequestsSceneUpdate()
//{
  //this->m_signal_request_scene_update();
//}

void ribi::cmap::QtNode::paint(
  QPainter* painter, const QStyleOptionGraphicsItem* item, QWidget* widget
) noexcept
{
  //this->m_display_strategy->SetName(this->GetNode()->GetConcept()->GetName());


  //Only QtEditStrategy actually modifies the position of the concept items
  /*
  if (dynamic_cast<QtEditStrategy*>(m_display_strategy.get()))
  {
    //Notifies the GUI-independent collaborators
    this->m_display_strategy->SetPos(x(),y());
  }
  */

  Base::paint(painter,item,widget);
  /*
  if (!GetNode()->GetConcept()->GetExamples()->Get().empty())
  {
    painter->setBrush(m_display_strategy->GetIndicatorBrush());
    painter->setPen(m_display_strategy->GetIndicatorPen());
    //Draw indicator that a concept has examples in it
    painter->drawRect(
      GetRect().right() - 5.0,
      GetRect().top() + 3.0,
      3.0,
      3.0
      );
  }
  */

  //Check if item can move (as the center node cannot)
  #ifdef BRAINWEAVER_MOVE_ITEMS_ON_COLLISION
  if (this->flags() & QGraphicsItem::ItemIsMovable)
  {
    //Item can move, check for collision
    const QList<QGraphicsItem*> others = collidingItems();
    std::for_each(others.begin(),others.end(),
      [this](const QGraphicsItem* const other_item)
      {
        assert(other_item);
        if (const QtNode* const other_node = dynamic_cast<const QtNode*>(other_item))
        {
          const double dx = x() - other_node->x() > 0.0 ? 1.0 : -1.0;
          const double dy = y() - other_node->y() > 0.0 ? 1.0 : -1.0;
          //assert(this->flags() & QGraphicsItem::ItemIsMovable); //Not true for center node
          this->setPos( this->x()  + dx, this->y()  + dy);
        }
      }
    );
  }
  #endif
  if (m_show_bounding_rect)
  {
    const QPen prev_pen = painter->pen();
    const QBrush prev_brush = painter->brush();
    painter->setPen(QPen(QColor(0,0,96)));
    painter->setBrush(QBrush(QColor(0,0,255,64)));
    painter->drawRect(this->boundingRect().adjusted(1.0,1.0,-1.0,-1.0));
    painter->setPen(prev_pen);
    painter->setBrush(prev_brush);
  }
}

void ribi::cmap::QtNode::SetNode(const boost::shared_ptr<Node>& node) noexcept
{
  const bool verbose{false};

  assert(node);
  if (m_node == node)
  {
    return;
  }

  if (verbose)
  {
    std::stringstream s;
    s << "Setting node '" << node->ToStr() << "'\n";
  }
  const auto concept_after = node->GetConcept();
  const auto x_after = node->GetX();
  const auto y_after = node->GetY();

  bool concept_changed{true};
  bool x_changed{true};
  bool y_changed{true};

  if (m_node)
  {
    const auto concept_before = m_node->GetConcept();
    const auto x_before = m_node->GetX();
    const auto y_before = m_node->GetY();

    concept_changed = concept_before != concept_after;
    x_changed = x_before != x_after;
    y_changed = y_before != y_after;


    if (verbose)
    {
      if (concept_changed)
      {
        std::stringstream s;
        s
          << "Concept will change from "
          << concept_before->ToStr()
          << " to "
          << concept_after->ToStr()
          << '\n'
        ;
        TRACE(s.str());
      }
      if (x_changed)
      {
        std::stringstream s;
        s << "X will change from " << x_before
          << " to " << x_after << '\n';
        TRACE(s.str());
      }
      if (y_changed)
      {
        std::stringstream s;
        s << "Y will change from " << y_before
          << " to " << y_after << '\n';
        TRACE(s.str());
      }
    }
    //Disconnect m_concept
    m_node->m_signal_concept_changed.disconnect(
      boost::bind(&ribi::cmap::QtNode::OnConceptChanged,this,boost::lambda::_1)
    );
    m_node->m_signal_x_changed.disconnect(
      boost::bind(&ribi::cmap::QtNode::OnXchanged,this,boost::lambda::_1)
    );
    m_node->m_signal_y_changed.disconnect(
      boost::bind(&ribi::cmap::QtNode::OnYchanged,this,boost::lambda::_1)
    );
  }

  //Replace m_example by the new one
  m_node = node;


  assert(m_node->GetConcept() == concept_after );
  assert(m_node->GetX() == x_after );
  assert(m_node->GetY() == y_after);

  //SetPos(m_node->GetX(),m_node->GetY());
  //assert(GetPos().x() == m_node->GetX());
  //assert(GetPos().y() == m_node->GetY());

  m_node->m_signal_concept_changed.connect(
    boost::bind(&ribi::cmap::QtNode::OnConceptChanged,this,boost::lambda::_1)
  );
  m_node->m_signal_x_changed.connect(
    boost::bind(&ribi::cmap::QtNode::OnXchanged,this,boost::lambda::_1)
  );
  m_node->m_signal_y_changed.connect(
    boost::bind(&ribi::cmap::QtNode::OnYchanged,this,boost::lambda::_1)
  );

  //Emit everything that has changed
  if (concept_changed)
  {
    m_node->m_signal_concept_changed(m_node.get());
  }
  if (x_changed)
  {
    m_node->m_signal_x_changed(m_node.get());
  }
  if (y_changed)
  {
    m_node->m_signal_y_changed(m_node.get());
  }

  assert( node ==  m_node);
  assert(*node == *m_node);
}

/*
void ribi::cmap::QtNode::SetName(const std::string& name) noexcept
{
  m_node->GetConcept()->SetName(name);
}
*/




#ifndef NDEBUG
void ribi::cmap::QtNode::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  {
    Container();
    QtRoundedEditRectItem();
    QtNodeFactory().GetTest(1);
  }

  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  const double max_error = 2.0;

  if (verbose) { TRACE("QtNode can be converted to QtRoundedEditRectItem") }
  {
    const auto qtnode = QtNodeFactory().GetTest(1);
    const boost::shared_ptr<QtRoundedEditRectItem> edit_rect{boost::dynamic_pointer_cast<QtRoundedEditRectItem>(qtnode)};
    assert(edit_rect);
    assert(qtnode == edit_rect);
  }

  if (verbose) { TRACE("Test X coordinat in Node and QtRoundedEditRectItem being equal at creation") }
  {
    const auto qtnode = QtNodeFactory().GetTest(1);
    const boost::shared_ptr<QtRoundedEditRectItem> edit_rect{boost::dynamic_pointer_cast<QtRoundedEditRectItem>(qtnode)};
    const auto node = qtnode->GetNode();
    const double node_x = node->GetX();
    const double edit_rect_x = edit_rect->GetCenterX();
    assert(std::abs(node_x - edit_rect_x) < max_error);
  }
  if (verbose) { TRACE("Test Y coordinat in Node and QtRoundedEditRectItem being equal at creation") }
  {
    const auto qtnode = QtNodeFactory().GetTest(1);
    const boost::shared_ptr<QtRoundedEditRectItem> edit_rect{boost::dynamic_pointer_cast<QtRoundedEditRectItem>(qtnode)};
    const auto node = qtnode->GetNode();
    const double node_y = node->GetY();
    const double edit_rect_y = edit_rect->GetCenterY();
    assert(std::abs(node_y - edit_rect_y) < max_error);
  }
  if (verbose) { TRACE("Test text in Node and QtRoundedEditRectItem being equal at creation") }
  {
    const auto qtnode = QtNodeFactory().GetTest(1);
    const boost::shared_ptr<QtRoundedEditRectItem> edit_rect{boost::dynamic_pointer_cast<QtRoundedEditRectItem>(qtnode)};
    const auto node = qtnode->GetNode();
    const std::string node_text{node->GetConcept()->GetName()};
    const std::string edit_rect_text{Container().Concatenate(edit_rect->GetText())};
    if (node_text != edit_rect_text)
    {
      TRACE(node_text);
      TRACE(edit_rect_text);
    }
    assert(node_text == edit_rect_text);
  }
  if (verbose) {TRACE("When changing the concept's name via Node, the QtNode must be changed as well");}
  {
    const auto qtnode = QtNodeFactory().GetTest(1);
    const boost::shared_ptr<QtRoundedEditRectItem> qtrectitem{boost::dynamic_pointer_cast<QtRoundedEditRectItem>(qtnode)};
    const std::string old_name{qtnode->GetNode()->GetConcept()->GetName()};
    const std::string new_name{old_name + " (modified)"};
    qtnode->GetNode()->GetConcept()->SetName(new_name);
    const auto v = qtrectitem->GetText();
    const auto t = v[0];
    assert(t == new_name);
  }
  if (verbose) {TRACE("When changing the concept's name via QtNode, the Node must be changed as well");}
  {
    const auto qtnode = QtNodeFactory().GetTest(1);
    const boost::shared_ptr<QtRoundedEditRectItem> qtrectitem{boost::dynamic_pointer_cast<QtRoundedEditRectItem>(qtnode)};
    const std::string old_name = qtrectitem->GetText()[0];
    const std::string new_name{old_name + " (modified)"};
    qtrectitem->SetText( { new_name } );
    const std::string new_name_again{qtnode->GetNode()->GetConcept()->GetName()};
    assert(new_name_again == new_name);
  }


  #ifdef DISABLED_FOR_NOW_20140730
  assert(flags() & QGraphicsItem::ItemIsFocusable);
  assert(flags() & QGraphicsItem::ItemIsSelectable);
  assert(flags() & QGraphicsItem::ItemIsMovable);
  assert(this->acceptHoverEvents()); //Must remove the 's' in Qt5?

  {
    {
      const double new_x = 12.34;
      const double new_y = 43.21;

      //Change via node
      assert(node);
      node->SetX(new_x);
      node->SetY(new_y);

      const double node_x = node->GetX();
      const double qtnode_x = qtnode->GetPos().x();

      if (std::abs(node_x - qtnode_x) >= max_error)
      {
        TRACE(node_x);
        TRACE(qtnode_x);
      }
      assert(std::abs(node_x - qtnode_x) < max_error
       && "X coordinat must be in sync");
      const double node_y = node->GetY();
      const double qtnode_y = qtnode->GetPos().y();

      assert(node_y == qtnode_y
       && "Y coordinat must be in sync");
    }
    //Change via Qt node
    {
      const double new_x = 123.456;
      const double new_y = 654.321;

      qtnode->SetPos(new_x,new_y);

      const double node_x = node->GetX();
      const double qtnode_x = qtnode->GetPos().x();

      assert(std::abs(node_x - qtnode_x) < max_error
       && "X coordinat must be in sync");
      const double node_y = node->GetY();
      const double qtnode_y = qtnode->GetPos().y();

      assert(std::abs(node_y - qtnode_y) < max_error
       && "Y coordinat must be in sync");
    }

  }
  #endif
}
#endif

std::string ribi::cmap::QtNode::ToStr() const noexcept
{
  std::stringstream s;
  s << (*this);
  return s.str();
}

std::ostream& ribi::cmap::operator<<(std::ostream& os, const QtNode& qtnode) noexcept
{
  os
    << (*qtnode.GetNode())
    //<< " (" << (*qtnode.GetDisplayStrategy()) << ")"
  ;
  return os;
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtnodedialog.h

 

#ifndef QTCONCEPTMAPQTNODEDIALOG_H
#define QTCONCEPTMAPQTNODEDIALOG_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop


namespace Ui {
  class QtQtNodeDialog;
}

namespace ribi {
namespace cmap {

class QtQtNodeDialog : public ::ribi::QtHideAndShowDialog
{
  Q_OBJECT

public:
  explicit QtQtNodeDialog(QWidget *parent = 0);
  QtQtNodeDialog(const QtQtNodeDialog&) = delete;
  QtQtNodeDialog& operator=(const QtQtNodeDialog&) = delete;
  ~QtQtNodeDialog();


  static int GetMinimumHeight(const QtNode& qtnode) noexcept;
  boost::shared_ptr<QtNode> GetQtNode() const noexcept { return m_qtnode; }

  ///Read the name from the GUI
  std::string GetUiName() const noexcept;
  ///Read the X from the GUI
  double GetUiX() const noexcept;
  ///Read the Y from the GUI
  double GetUiY() const noexcept;

  void SetQtNode(const boost::shared_ptr<QtNode>& qtnode) noexcept;

  ///Set the name via the GUI
  void SetUiName(const std::string& name) noexcept;
  ///Set the X via the GUI
  void SetUiX(const double x) noexcept;
  ///Set the Y via the GUI
  void SetUiY(const double y) noexcept;

private:
  Ui::QtQtNodeDialog *ui;

  ///The QtNode to work on
  ///A QtNode is
  /// (1) a Node
  /// (2) inherited properties from QtRoundedEditRectItem
  boost::shared_ptr<QtNode> m_qtnode;

  ///QtDialog that displays (1) a Node
  boost::shared_ptr<QtNodeDialog> m_qtnodedialog;

  ///QtDialog that displays (2) inherited properties from QtRoundedEditRectItem
  boost::shared_ptr<QtRoundedEditRectItemDialog> m_qtroundededitrectitem_dialog;

  //void OnDisplayStrategyChanged(QtNode * const qtnode) noexcept;
  void OnNodeChanged(QtNode * const qtnode) noexcept;
  void OnQtRoundedRectItemChanged(QtNode * const qtnode) noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPQTNODEDIALOG_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtnodedialog.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmapqtnodedialog.h"

#include <cassert>
#include <sstream>

#include <boost/lambda/lambda.hpp>

#include <QDesktopWidget>
#include <QVBoxLayout>

#include "conceptmapnode.h"
#include "conceptmapnodefactory.h"
#include "qtconceptmapnodedialog.h"
#include "qtconceptmapqtnodefactory.h"
#include "qtconceptmapqtnode.h"
#include "qtitemdisplaystrategy.h"
#include "qtroundededitrectitemdialog.h"

#include "testtimer.h"
#include "trace.h"

#include "ui_qtconceptmapqtnodedialog.h"
//#include "ui_qtconceptmapnodedialog.h"
//#include "ui_qtroundededitrectitemdialog.h"
#pragma GCC diagnostic pop

ribi::cmap::QtQtNodeDialog::QtQtNodeDialog(QWidget *parent)
  : QtHideAndShowDialog(parent),
  ui(new Ui::QtQtNodeDialog),
  m_qtnode{},
  m_qtnodedialog{},
  m_qtroundededitrectitem_dialog{}
{
  #ifndef NDEBUG
  Test();
  #endif
  ui->setupUi(this);
  {
    assert(!this->layout());
    QVBoxLayout * const my_layout{new QVBoxLayout};
    this->setLayout(my_layout);
  }
  {
    assert(this->layout());
    const boost::shared_ptr<QtNodeDialog> d{new QtNodeDialog};
    assert(d);
    m_qtnodedialog = d;
    this->layout()->addWidget(m_qtnodedialog.get());
  }
  {
    assert(this->layout());
    const boost::shared_ptr<QtRoundedEditRectItemDialog> d{new QtRoundedEditRectItemDialog};
    m_qtroundededitrectitem_dialog = d;
    this->layout()->addWidget(m_qtroundededitrectitem_dialog.get());
  }

  {
    //Put the dialog in the screen center
    const QRect screen = QApplication::desktop()->screenGeometry();
    this->setGeometry(0,0,screen.width() * 9 / 10, screen.height() * 9 / 10);
    this->move( screen.center() - this->rect().center() );
  }
}

ribi::cmap::QtQtNodeDialog::~QtQtNodeDialog()
{
  delete ui;
}

int ribi::cmap::QtQtNodeDialog::GetMinimumHeight(const QtNode& qtnode) noexcept
{
  const int margin = 16;
  return
    QtNodeDialog::GetMinimumHeight(*qtnode.GetNode())
  + margin
  + QtRoundedEditRectItemDialog::GetMinimumHeight(qtnode)
  ;
}

std::string ribi::cmap::QtQtNodeDialog::GetUiName() const noexcept
{
  return m_qtnodedialog->GetUiName();
}

double ribi::cmap::QtQtNodeDialog::GetUiX() const noexcept
{
  return m_qtnodedialog->GetUiX();
}

double ribi::cmap::QtQtNodeDialog::GetUiY() const noexcept
{
  return m_qtnodedialog->GetUiY();
}

void ribi::cmap::QtQtNodeDialog::OnNodeChanged(QtNode * const
#ifndef NDEBUG
  qtnode
#endif // NDEBUG
) noexcept
{
  assert( qtnode ==  m_qtnode.get());
  assert(*qtnode == *m_qtnode);
  m_qtroundededitrectitem_dialog->SetItem(m_qtnode);
}

void ribi::cmap::QtQtNodeDialog::OnQtRoundedRectItemChanged(QtNode * const qtnode) noexcept
{
  //Emit
  m_qtnodedialog->SetNode(qtnode->GetNode());

  /*
  if (node == m_node) return;
  if (*node == *m_node) return;
  assert(std::abs(m_node->GetX() - this->GetPos().x()) < 2.0); //Allow two maximal rounding off errors
  assert(std::abs(m_node->GetY() - this->GetPos().y()) < 2.0); //Allow two maximal rounding off errors
  //if (node->GetX() != m_node->GetX())
  {
    //const double new_x = node->GetX();
    //m_node->SetX(new_x);
    this->SetPos(node->GetX(),this->GetPos().y());
  }
  //if (node->GetY() != m_node->GetY())
  {
    //const double new_y = node->GetY();
    //m_node->SetY(new_y);
    this->SetPos(this->GetPos().x(),node->GetY());
  }
  //if (node->GetConcept()->GetName() != m_node->GetConcept()->GetName())
  {
    //const auto new_text = node->GetConcept()->GetName();
    //m_node->GetConcept()->SetName(new_text);
    this->SetText(Container().SeperateString(node->GetConcept()->GetName(),'\n'));
  }
  */
}

void ribi::cmap::QtQtNodeDialog::SetQtNode(const boost::shared_ptr<QtNode>& qtnode) noexcept
{
  const bool verbose{false};

  assert(qtnode);

  if (m_qtnode == qtnode)
  {
    return;
  }

  if (verbose)
  {
    std::stringstream s;
    s << "Setting node '" << qtnode->ToStr() << "'\n";
  }

  const auto qtroundededitrectitem_after = qtnode.get();
  const auto node_after = qtnode->GetNode();

  bool qtroundededitrectitem_changed = true;
  bool node_changed = true;

  if (m_qtnode)
  {
    const auto qtroundededitrectitem_before = m_qtnode.get();
    const auto node_before = m_qtnode->GetNode();

    qtroundededitrectitem_changed = qtroundededitrectitem_before != qtroundededitrectitem_after;
    node_changed = node_before != node_after;


    if (verbose)
    {
      if (qtroundededitrectitem_changed)
      {
        std::stringstream s;
        s
          << "DisplayStrategy will change from "
          << qtroundededitrectitem_before->ToStr()
          << " to "
          << qtroundededitrectitem_after->ToStr()
          << '\n'
        ;
        TRACE(s.str());
      }
      if (node_changed)
      {
        std::stringstream s;
        s << "QtNode will change from " << (*node_before)
          << " to " << (*node_after) << '\n';
        TRACE(s.str());
      }
    }
    //Disconnect m_concept
    m_qtnode->m_signal_base_changed.disconnect(
      boost::bind(&ribi::cmap::QtQtNodeDialog::OnQtRoundedRectItemChanged,this,boost::lambda::_1)
    );
    m_qtnode->m_signal_node_changed.disconnect(
      boost::bind(&ribi::cmap::QtQtNodeDialog::OnNodeChanged,this,boost::lambda::_1)
    );
  }

  //Replace m_example by the new one
  m_qtnode = qtnode;

  assert(m_qtnode->GetNode() == node_after);

  m_qtnode->m_signal_base_changed.connect(
    boost::bind(&ribi::cmap::QtQtNodeDialog::OnQtRoundedRectItemChanged,this,boost::lambda::_1)
  );
  m_qtnode->m_signal_node_changed.connect(
    boost::bind(&ribi::cmap::QtQtNodeDialog::OnNodeChanged,this,boost::lambda::_1)
  );

  //Emit everything that has changed
  if (qtroundededitrectitem_changed)
  {
    m_qtnode->m_signal_base_changed(m_qtnode.get());
  }
  if (node_changed)
  {
    m_qtnode->m_signal_node_changed(m_qtnode.get());
  }

  this->setMinimumHeight(
    this->GetMinimumHeight(
      *m_qtnode
    )
  );

  assert( qtnode ==  m_qtnode);
  assert(*qtnode == *m_qtnode);
}

void ribi::cmap::QtQtNodeDialog::SetUiName(const std::string& name) noexcept
{
  m_qtnodedialog->SetUiName(name);
}

void ribi::cmap::QtQtNodeDialog::SetUiX(const double x) noexcept
{
  m_qtnodedialog->SetUiX(x);
}

void ribi::cmap::QtQtNodeDialog::SetUiY(const double y) noexcept
{
  m_qtnodedialog->SetUiY(y);
}

#ifndef NDEBUG
void ribi::cmap::QtQtNodeDialog::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  {
    QtNodeFactory();
    QtNodeFactory().GetTest(1);
    QtNodeDialog();
    QtRoundedEditRectItemDialog();
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  QtQtNodeDialog dialog;
  const auto node = NodeFactory().GetTest(1);
  const boost::shared_ptr<QtNode> qtnode{new QtNode(node)};
  dialog.SetQtNode(qtnode);
  if (verbose) { TRACE("SetUiX and GetUiX must be symmetric"); }
  {
    const double old_x{dialog.GetUiX()};
    const double new_x{old_x + 10.0};
    dialog.SetUiX(new_x);
    assert(std::abs(dialog.GetUiX() - new_x) < 2.0);
  }
  if (verbose) { TRACE("SetUiY and GetUiY must be symmetric"); }
  {
    const double old_y{dialog.GetUiY()};
    const double new_y{old_y + 10.0};
    dialog.SetUiY(new_y);
    assert(std::abs(dialog.GetUiY() - new_y) < 2.0);
  }
}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtnodefactory.h

 

#ifndef QTCONCEPTMAPQTNODEFACTORY_H
#define QTCONCEPTMAPQTNODEFACTORY_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace ribi {
namespace cmap {

struct QtNodeFactory
{
  QtNodeFactory();

  boost::shared_ptr<QtNode> Create(
    const boost::shared_ptr<Node>& node
  ) const noexcept;


  ///Obtain a Node or CenterNode from an XML std::string
  boost::shared_ptr<Node> FromXml(const std::string& s) const noexcept;

  ///Obtain testing nodes
  int GetNumberOfTests() const noexcept;
  std::vector<boost::shared_ptr<QtNode>> GetTests() const noexcept;
  boost::shared_ptr<QtNode> GetTest(const int test) const noexcept;

  private:
  #ifndef NDEBUG
  void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPQTNODEFACTORY_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmapqtnodefactory.cpp

 

#include "qtconceptmapqtnodefactory.h"

#include <cassert>

#include "conceptmapnodefactory.h"
#include "qtconceptmapqtnode.h"

#include "testtimer.h"
#include "trace.h"

ribi::cmap::QtNodeFactory::QtNodeFactory()
{
  #ifndef NDEBUG
  Test();
  #endif
}

boost::shared_ptr<ribi::cmap::QtNode> ribi::cmap::QtNodeFactory::Create(
  const boost::shared_ptr<Node>& node
) const noexcept
{
  assert(node);
  boost::shared_ptr<QtNode> qtnode{new QtNode(node)};
  assert(qtnode);
  return qtnode;
}

int ribi::cmap::QtNodeFactory::GetNumberOfTests() const noexcept
{
  const int n{static_cast<int>(GetTests().size())};
  return n;
}

boost::shared_ptr<ribi::cmap::QtNode> ribi::cmap::QtNodeFactory::GetTest(const int i) const noexcept
{
  const auto tests = GetTests();
  assert(i >= 0);
  assert(i < static_cast<int>(tests.size()));
  return tests[i];
}

std::vector<boost::shared_ptr<ribi::cmap::QtNode>> ribi::cmap::QtNodeFactory::GetTests() const noexcept
{
  std::vector<boost::shared_ptr<QtNode>> qtnodes;
  const auto v = NodeFactory().GetTests();
  std::transform(v.begin(),v.end(),std::back_inserter(qtnodes),
    [](const boost::shared_ptr<Node>& c)
    {
      const boost::shared_ptr<QtNode> q{QtNodeFactory().Create(c)};
      assert(q);
      return q;
    }
  );
  return qtnodes;
}

#ifndef NDEBUG
void ribi::cmap::QtNodeFactory::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  QtNodeFactory().GetTest(0);

  const TestTimer test_timer(__func__,__FILE__,1.0);
  const bool verbose{false};
  const QtNodeFactory f;
  if (verbose) { TRACE("Create all QtNodes") }
  {
    const int n = f.GetNumberOfTests();
    for (int i=0; i!=n; ++i)
    {
      const auto qtnode = f.GetTest(i);
      assert(qtnode);
    }
  }
}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmaprateconceptdialognewname.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPRATECONCEPTDIALOG_H
#define QTCONCEPTMAPRATECONCEPTDIALOG_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"

#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace Ui { class QtRateConceptDialogNewName; }
namespace ribi {
namespace cmap {

///Rate the focal concept of a sub-ConceptMap.
class QtRateConceptDialogNewName : public ribi::QtHideAndShowDialog
{
  Q_OBJECT
    
  public:
  ///concept is the center node
  ///sub_concept_map[0] is the same as concept and might be changed
  ///sub_concept_map is non-const, as GetRatedConcept will produce a new concept
  explicit QtRateConceptDialogNewName(const boost::shared_ptr<ConceptMap> sub_concept_map,
    QWidget* parent = 0);
  QtRateConceptDialogNewName(const QtRateConceptDialogNewName&) = delete;
  QtRateConceptDialogNewName& operator=(const QtRateConceptDialogNewName&) = delete;
  ~QtRateConceptDialogNewName() noexcept;

  ///Set suggested values for this concept
  //void MakeSuggestions(const boost::shared_ptr<const ConceptMap> sub_concept_map);

protected:
  void keyPressEvent(QKeyEvent *);

private slots:
  void on_button_ok_clicked();

  void on_button_tally_relevancies_clicked();

  void on_box_complexity_currentIndexChanged(int index);
  void on_box_concreteness_currentIndexChanged(int index);
  void on_box_specificity_currentIndexChanged(int index);

private:
  Ui::QtRateConceptDialogNewName *ui;

  ///To distinguish between closing the dialog by clicking OK, or by ALT-F4
  bool m_button_ok_clicked;

  ///The center concept, may be changed when the user clicks OK
  const boost::shared_ptr</* NO CONST */ Concept> m_concept;

  ///The complexity at this dialog its creation, stored so that the user can cancel the dialog
  const int m_initial_complexity;
  const int m_initial_concreteness;
  const int m_initial_specificity;

  ///Cannot be const, only used in calculating the suggestions
  const boost::shared_ptr<ConceptMap> m_sub_concept_map;

  const boost::shared_ptr<QtConceptMap> m_widget;
  //const boost::shared_ptr<QtRateConceptMap> m_widget;
  //QtConceptMapRateWidget * const m_widget; //WHY DID I DO THIS???

  void OnRatingComplexityChanged(const Concept* concept);
  void OnRatingConcretenessChanged(const Concept* concept);
  void OnRatingSpecificityChanged(const Concept* concept);

  #ifndef NDEBUG
  ///Test this class
  static void Test() noexcept;
  #endif
};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPRATECONCEPTDIALOG_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmaprateconceptdialognewname.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include "qtconceptmaprateconceptdialognewname.h"

#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <QDesktopWidget>
#include <QFileDialog>
#include <QKeyEvent>

#include "conceptmapconceptfactory.h"
#include "conceptmapconcept.h"
#include "conceptmapfactory.h"
#include "conceptmap.h"
#include "conceptmapedge.h"
#include "testtimer.h"
#include "conceptmapexamplesfactory.h"
#include "conceptmapexamples.h"
//#include "conceptmapfile.h"
#include "conceptmapnodefactory.h"
#include "conceptmapnode.h"
#include "qtrateconceptmap.h"
#include "qtconceptmaprating.h"
#include "qtrateconceptmap.h"
#include "qtconceptmaprateconcepttallydialognewname.h"
#include "trace.h"
#include "ui_qtconceptmaprateconceptdialognewname.h"
#pragma GCC diagnostic pop

ribi::cmap::QtRateConceptDialogNewName::QtRateConceptDialogNewName(
  const boost::shared_ptr<ribi::cmap::ConceptMap> sub_concept_map,
  QWidget* parent)
  : QtHideAndShowDialog(parent),
    ui(new Ui::QtRateConceptDialogNewName),
    m_button_ok_clicked(false),
    m_concept(sub_concept_map
      ? sub_concept_map->GetFocalNode()->GetConcept()
      : boost::shared_ptr<Concept>() ),
    m_initial_complexity(sub_concept_map
      ? sub_concept_map->GetFocalNode()->GetConcept()->GetRatingComplexity()
      : -1 ),
    m_initial_concreteness(sub_concept_map
      ? sub_concept_map->GetFocalNode()->GetConcept()->GetRatingConcreteness()
      : -1),
    m_initial_specificity(sub_concept_map
      ? sub_concept_map->GetFocalNode()->GetConcept()->GetRatingSpecificity()
      : -1),

    m_sub_concept_map(sub_concept_map),
    m_widget(new QtRateConceptMap)
{
  ui->setupUi(this);
  #ifndef NDEBUG
  Test();
  #endif
  if (!m_sub_concept_map) return;
  m_widget->SetConceptMap(sub_concept_map);
  assert(m_sub_concept_map);
  assert(!m_sub_concept_map->GetNodes().empty());

  assert(m_widget);
  assert(ui->concept_map_layout);

  ui->concept_map_layout->addWidget(m_widget.get());

  assert(m_concept);

  ui->box_complexity->setCurrentIndex(m_initial_complexity);
  ui->box_concreteness->setCurrentIndex(m_initial_concreteness);
  ui->box_specificity->setCurrentIndex(m_initial_specificity);
  ui->box_complexity->setFocus();

  //Set suggestions
  {
    const std::string s = "Formeel uitgangspunt: "
      + boost::lexical_cast<std::string>(cmap::Rating::SuggestComplexity(m_sub_concept_map));
    ui->box_complexity->setToolTip(s.c_str());
  }
  {
    const std::string s = "Formeel uitgangspunt: "
      + boost::lexical_cast<std::string>(cmap::Rating::SuggestConcreteness(m_sub_concept_map));
    ui->box_concreteness->setToolTip(s.c_str());
  }
  {
    const std::string s = "Formeel uitgangspunt: "
      + boost::lexical_cast<std::string>(cmap::Rating::SuggestSpecificity(m_sub_concept_map));
    ui->box_specificity->setToolTip(s.c_str());
  }

  //Center the dialog
  {
    const QRect screen = QApplication::desktop()->screenGeometry();
    this->setGeometry(screen.adjusted(64,64,-64,-64));
    this->move( screen.center() - this->rect().center() );
  }
  //The rating by the Tally dialog must be visible as of 2013-08-30
  //so let this dialog follow the ratings done by the tally dialog
  //DOES NOT WORK
  //m_concept->m_signal_rating_complexity_changed.connect(
  //  boost::bind(&QtRateStrategyDialog::OnRatingComplexityChanged,this,boost::lambda::_1));
  //m_concept->m_signal_rating_concreteness_changed.connect(
  //  boost::bind(&QtRateStrategyDialog::OnRatingConcretenessChanged,this,boost::lambda::_1));
  //m_concept->m_signal_rating_specificity_changed.connect(
  //  boost::bind(&QtRateStrategyDialog::OnRatingSpecificityChanged,this,boost::lambda::_1));
}



ribi::cmap::QtRateConceptDialogNewName::~QtRateConceptDialogNewName() noexcept
{
  //If user clicked OK, keep the current ratings (which are updated by the comboboxes)
  //else the user cancelled, so put back the initial ratings
  if (!m_button_ok_clicked && m_concept)
  {
    m_concept->SetRatingComplexity(m_initial_complexity);
    m_concept->SetRatingConcreteness(m_initial_concreteness);
    m_concept->SetRatingSpecificity(m_initial_specificity);
  }
  //if (m_concept)
  //{
  //  //Just to be sure
  //  m_concept->m_signal_rating_complexity_changed.disconnect(
  //    boost::bind(&QtRateStrategyDialog::OnRatingComplexityChanged,this,boost::lambda::_1));
  //  m_concept->m_signal_rating_concreteness_changed.disconnect(
  //    boost::bind(&QtRateStrategyDialog::OnRatingConcretenessChanged,this,boost::lambda::_1));
  //  m_concept->m_signal_rating_specificity_changed.disconnect(
  //    boost::bind(&QtRateStrategyDialog::OnRatingSpecificityChanged,this,boost::lambda::_1));
  //}
  delete ui;
}

void ribi::cmap::QtRateConceptDialogNewName::keyPressEvent(QKeyEvent* e)
{
  if (e->key()  == Qt::Key_Escape) { close(); return; }
  if ( (e->modifiers() & Qt::ControlModifier)
    && (e->modifiers() & Qt::ShiftModifier)
    && e->key() == Qt::Key_T)
  {
    //Translate
    this->setWindowTitle("Assess cluster");
    ui->button_ok->setText("OK");
    ui->button_tally_relevancies->setText("Evaluate illustrations");
    ui->label_complexity->setText("Complexity");
    ui->label_concreteness->setText("Concreteness");
    ui->label_specificity->setText("Specificity");
/*

De relevantie van voorbeelden en relaties Relevance of illustrations
Voorbeelden/toelichting bij concept: Illustrations and relations of the cluster:

*/
    return;
  }

  //QDialog::keyPressEvent(e);
}

void ribi::cmap::QtRateConceptDialogNewName::on_button_ok_clicked()
{
  //Ratings already set by comboboxes
  m_button_ok_clicked = true;
  close();
}

void ribi::cmap::QtRateConceptDialogNewName::OnRatingComplexityChanged(const ribi::cmap::Concept* concept)
{
  assert(concept);
  if (ui->box_complexity->currentIndex() != concept->GetRatingComplexity())
  {
    ui->box_complexity->setCurrentIndex(concept->GetRatingComplexity());
  }
}

void ribi::cmap::QtRateConceptDialogNewName::OnRatingConcretenessChanged(const ribi::cmap::Concept* concept)
{
  assert(concept);
  if (ui->box_concreteness->currentIndex() != concept->GetRatingConcreteness())
  {
    ui->box_concreteness->setCurrentIndex(concept->GetRatingConcreteness());
  }
}

void ribi::cmap::QtRateConceptDialogNewName::OnRatingSpecificityChanged(const ribi::cmap::Concept* concept)
{
  assert(concept);
  if (ui->box_specificity->currentIndex() != concept->GetRatingSpecificity())
  {
    ui->box_specificity->setCurrentIndex(concept->GetRatingSpecificity());
  }
}

#ifndef NDEBUG
void ribi::cmap::QtRateConceptDialogNewName::Test() noexcept
{
  {
    static bool is_tested = false;
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer{__func__,__FILE__,0.1};
  #ifdef RJCB_TODO //TODO RJCB: Put back in
  {
    const std::vector<boost::shared_ptr<ConceptMap> > concept_maps
      = ConceptMapFactory().GetAllTests();
    const std::size_t n_concept_maps = concept_maps.size();
    for (std::size_t i=0; i!=n_concept_maps; ++i)
    {
      const boost::shared_ptr<ConceptMap> concept_map = concept_maps[i];
      if (!concept_map)
      {
        QtRateConceptDialogNewName d(concept_map);
        continue;
      }
      assert(concept_map);
      const boost::shared_ptr<Concept> concept = concept_map->GetFocalNode()->GetConcept();
      assert(concept);
      const boost::shared_ptr<Concept> old_concept = ConceptFactory().DeepCopy(concept);
      assert(old_concept);
      assert(concept != old_concept);
      assert(*concept == *old_concept);
      {
        QtRateConceptDialogNewName d(concept_map);
        assert(concept->GetRatingComplexity() == d.ui->box_complexity->currentIndex());
        assert(concept->GetRatingConcreteness() == d.ui->box_concreteness->currentIndex());
        assert(concept->GetRatingSpecificity() == d.ui->box_specificity->currentIndex());
        //Change all boxes
        d.ui->box_complexity->setCurrentIndex(((d.ui->box_complexity->currentIndex() + 2) % 4) - 1);
        d.ui->box_concreteness->setCurrentIndex(((d.ui->box_complexity->currentIndex() + 2) % 4) - 1);
        d.ui->box_specificity->setCurrentIndex(((d.ui->box_complexity->currentIndex() + 2) % 4) - 1);
        //But do not click OK
        d.close();
        //Need to call the destructor
      }
      assert(*concept == *old_concept && "Without clicking OK, QtRateStrategyDialog must not change the concept");
    }
  }
  {
    const std::vector<boost::shared_ptr<ConceptMap> > concept_maps
      = ConceptMapFactory().GetAllTests();
    const std::size_t n_concept_maps = concept_maps.size();
    for (std::size_t i=0; i!=n_concept_maps; ++i)
    {
      const boost::shared_ptr<ConceptMap> concept_map = concept_maps[i];
      if (!concept_map)
      {
        QtRateConceptDialogNewName d(concept_map);
        continue;
      }
      assert(concept_map);
      const boost::shared_ptr<Concept> concept = concept_map->GetFocalNode()->GetConcept();
      assert(concept);
      const boost::shared_ptr<const Concept> old_concept = ConceptFactory().DeepCopy(concept);

      assert(old_concept);
      assert(concept != old_concept);
      assert(*concept == *old_concept);
      QtRateConceptDialogNewName d(concept_map);
      assert(concept->GetRatingComplexity()   == d.ui->box_complexity->currentIndex());
      assert(concept->GetRatingConcreteness() == d.ui->box_concreteness->currentIndex());
      assert(concept->GetRatingSpecificity()  == d.ui->box_specificity->currentIndex());
      //Change all boxes, in range [-1,2]
      d.ui->box_complexity->setCurrentIndex(((d.ui->box_complexity->currentIndex() + 2) % 4) - 1);
      d.ui->box_concreteness->setCurrentIndex(((d.ui->box_complexity->currentIndex() + 2) % 4) - 1);
      d.ui->box_specificity->setCurrentIndex(((d.ui->box_complexity->currentIndex() + 2) % 4) - 1);
      d.ui->button_ok->click();
      assert(*concept != *old_concept && "QtRateStrategyDialog must change the concept when clicked OK");
    }
  }
  #endif

}
#endif

void ribi::cmap::QtRateConceptDialogNewName::on_button_tally_relevancies_clicked()
{
  #ifndef NDEBUG
  const bool has_concept_map = m_sub_concept_map.get(); //.get() needed for crosscompiler
  #endif
   QtRateConceptTallyDialogNewName d(m_sub_concept_map);
  d.exec(); //Keep this dialog visible, as of 2013-08-30
  assert(has_concept_map == static_cast<bool>(m_sub_concept_map.get()));
  ui->box_complexity->setCurrentIndex(d.GetSuggestedComplexity());
  ui->box_concreteness->setCurrentIndex(d.GetSuggestedConcreteness());
  ui->box_specificity->setCurrentIndex(d.GetSuggestedSpecificity());
}

void ribi::cmap::QtRateConceptDialogNewName::on_box_complexity_currentIndexChanged(int)
{
  assert(m_concept);
  if (m_concept->GetRatingComplexity() != ui->box_complexity->currentIndex())
  {
    m_concept->SetRatingComplexity(ui->box_complexity->currentIndex());
  }
}

void ribi::cmap::QtRateConceptDialogNewName::on_box_concreteness_currentIndexChanged(int)
{
  assert(m_concept);
  if (m_concept->GetRatingConcreteness() != ui->box_concreteness->currentIndex())
  {
    m_concept->SetRatingConcreteness(ui->box_concreteness->currentIndex());
  }
}

void ribi::cmap::QtRateConceptDialogNewName::on_box_specificity_currentIndexChanged(int)
{
  assert(m_concept);
  if (m_concept->GetRatingSpecificity() != ui->box_specificity->currentIndex())
  {
    m_concept->SetRatingSpecificity(ui->box_specificity->currentIndex());
  }
}

 

 

 

 

 

./CppQtConceptMap/qtconceptmaprateconcepttallydialognewname.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPRATECONCEPTTALLYDIALOG_H
#define QTCONCEPTMAPRATECONCEPTTALLYDIALOG_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include "qthideandshowdialog.h"
#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace Ui { class QtRateConceptTallyDialogNewName; }

namespace ribi {
namespace cmap {

class QtRateConceptTallyDialogNewName : public ribi::QtHideAndShowDialog
{
  Q_OBJECT
  
public:
  ///Sub concept map is modified by this dialog
  explicit QtRateConceptTallyDialogNewName(
    const boost::shared_ptr</* const */ ConceptMap> sub_concept_map,
    QWidget *parent = 0);
  QtRateConceptTallyDialogNewName(const QtRateConceptTallyDialogNewName&) = delete;
  QtRateConceptTallyDialogNewName& operator=(const QtRateConceptTallyDialogNewName&) = delete;
  ~QtRateConceptTallyDialogNewName() noexcept;

  static const boost::shared_ptr<ConceptMap> CreateTestConceptMap();

  ///Obtain the suggested complexity, calculated from this dialog
  int GetSuggestedComplexity() const;

  ///Obtain the suggested concreteness, calculated from this dialog
  int GetSuggestedConcreteness() const;

  ///Obtain the suggested specificity, calculated from this dialog
  int GetSuggestedSpecificity() const;

protected:
  void keyPressEvent(QKeyEvent *);
  void resizeEvent(QResizeEvent *);
private slots:
  void on_button_ok_clicked();
  void OnCellChanged(int row, int col);

private:
  Ui::QtRateConceptTallyDialogNewName *ui;
  //const boost::shared_ptr</* const */ ConceptMap> m_map;

  ///The concept map is converted to this data type
  ///The std::vector index equals the row
  ///Every row is a pair of a boost::shared_ptr<Concept> and an integer
  ///The boost::shared_ptr<Concept> is the concept being judged,
  ///  which might be the concept on the focal node and the concept on the egdes connected to the focal node
  ///The index is the index of the example being judged, or -1, denoting it is the concept name being judged
  //typedef std::pair<boost::shared_ptr<Concept>,int> Row;
  typedef std::tuple<boost::shared_ptr<const Edge>,boost::shared_ptr<Concept>,int> Row;
  const std::vector<Row> m_data;

  ///The name of this concept, for example 'my own development'
  const std::string m_focus_name;

  static std::vector<Row>
    CreateData(const boost::shared_ptr</* const */ ConceptMap> map);

  static std::string
    GetFocusName(const boost::shared_ptr<const ConceptMap> sub_concept_map) noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

};

} //~namespace cmap
} //~namespace ribi

#endif // QTCONCEPTMAPRATECONCEPTTALLYDIALOG_H

 

 

 

 

 

./CppQtConceptMap/qtconceptmaprateconcepttallydialognewname.cpp

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include "qtconceptmaprateconcepttallydialognewname.h"

#include <cassert>
#include <sstream>
#include <numeric>

#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <QKeyEvent>

#include "conceptmapconceptfactory.h"
#include "conceptmapcenternode.h"
#include "conceptmapcenternodefactory.h"
#include "conceptmap.h"
#include "conceptmapfactory.h"
#include "conceptmapconcept.h"
#include "conceptmapedge.h"
#include "conceptmapedgefactory.h"
#include "conceptmapnode.h"
#include "conceptmapnodefactory.h"
#include "conceptmapexample.h"
#include "conceptmapedge.h"
#include "conceptmapexamples.h"
#include "testtimer.h"
#include "qtconceptmaprating.h"
#include "trace.h"
#include "ui_qtconceptmaprateconcepttallydialognewname.h"
#pragma GCC diagnostic pop

ribi::cmap::QtRateConceptTallyDialogNewName::QtRateConceptTallyDialogNewName(
  const boost::shared_ptr</* const */ ribi::cmap::ConceptMap> sub_concept_map,
  QWidget *parent)
  : QtHideAndShowDialog(parent),
    ui(new Ui::QtRateConceptTallyDialogNewName),
    m_data(CreateData(sub_concept_map)),
    m_focus_name(GetFocusName(sub_concept_map))
{
  #ifndef NDEBUG
  Test();  
  #endif
  ui->setupUi(this);

  const int n_rows = static_cast<int>(m_data.size());
  const int n_cols = 4;
  ui->table->setRowCount(n_rows);
  ui->table->setWordWrap(true);

  //From https://stackoverflow.com/questions/9544122/how-to-word-wrap-text-in-the-rows-and-columns-of-a-qtablewidget
  connect(
    ui->table->horizontalHeader(),
    SIGNAL(sectionResized(int, int, int)),
    ui->table,
    SLOT(resizeRowsToContents()));

  for (int row_index=0; row_index!=n_rows; ++row_index)
  {
    const Row& row = m_data[row_index];
    const boost::shared_ptr<Concept> concept = std::get<1>(row);
    const int example_index = std::get<2>(row);

    assert(concept);
    if (example_index == -1)
    {
      //Display concept text
      //Put X checkbox in the relation's name
      //Keep C and S columns empty
      {
        //Put X checkbox in the relation's name in column[0]
        const int column = 0;
        QTableWidgetItem * const i = new QTableWidgetItem;
        i->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
        i->setCheckState(concept->GetIsComplex() ? Qt::Checked : Qt::Unchecked);
        ui->table->setItem(row_index, column, i);
      }
      {
        //Put uneditable nothing column[1]
        const int column = 1;
        QTableWidgetItem * const i = new QTableWidgetItem;
        i->setFlags(Qt::ItemIsEnabled);
        ui->table->setItem(row_index, column, i);
      }
      {
        //Put uneditable nothing column[2]
        const int column = 2;
        QTableWidgetItem * const i = new QTableWidgetItem;
        i->setFlags(Qt::ItemIsEnabled);
        ui->table->setItem(row_index, column, i);
      }
      {
        //Put the relation's name in place
        QTableWidgetItem * const i = new QTableWidgetItem;
        i->setFlags(
            Qt::ItemIsSelectable
          | Qt::ItemIsEnabled);
        const boost::shared_ptr<const Edge> edge { std::get<0>(row) };
        assert(edge);
        const bool center_is_from {
          edge->GetFrom()->GetConcept() == sub_concept_map->GetFocalNode()->GetConcept()
        };
        const boost::shared_ptr<const Node> other {
          center_is_from ? edge->GetTo() : edge->GetFrom()
        };
        const std::string s {
            "via '"
          + concept->GetName() + "' verbonden met '"
          + other->GetConcept()->GetName()
          + "'"
        };
        i->setText(s.c_str());

        const int column = 3;
        ui->table->setItem(row_index, column, i);
      }
    }
    else
    {
      assert(concept->GetExamples());
      assert(example_index < static_cast<int>(concept->GetExamples()->Get().size()));
      const boost::shared_ptr<cmap::Example> example = concept->GetExamples()->Get()[example_index];
      //Display index'th example
      for (int col_index=0; col_index!=n_cols; ++col_index)
      {
        if (col_index != 3)
        {
          QTableWidgetItem * const item = new QTableWidgetItem;
          item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
          switch (col_index)
          {
            case 0: item->setCheckState(example->GetIsComplex() ? Qt::Checked : Qt::Unchecked); break;
            case 1: item->setCheckState(example->GetIsConcrete() ? Qt::Checked : Qt::Unchecked); break;
            case 2: item->setCheckState(example->GetIsSpecific() ? Qt::Checked : Qt::Unchecked); break;
            default: assert(!"Should not get here");
          }
          ui->table->setItem(row_index, col_index, item);
        }
        else
        {
          //Text
          QTableWidgetItem * const item = new QTableWidgetItem;
          item->setFlags(
              Qt::ItemIsSelectable
            | Qt::ItemIsEnabled);
          const std::string s = example->GetText();
          item->setText(s.c_str());
          ui->table->setItem(row_index, col_index, item);
        }
      }
    }

  }

  //Set text on top
  ui->label_concept_name->setText(
    ("Voorbeelden/toelichting bij concept: " + m_focus_name).c_str()
  );

  ui->table->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

  ui->label_debug->setVisible(true);
  QObject::connect(ui->table,SIGNAL(cellChanged(int,int)),this,SLOT(OnCellChanged(int,int)));

  {
    const int x = GetSuggestedComplexity();
    const int c = GetSuggestedConcreteness();
    const int s = GetSuggestedSpecificity();
    std::stringstream m;
    m << "Complexiteit: " << x << ", concreetheid: " << c << ", specificiteit: " << s;
    ui->label_debug->setText(m.str().c_str());
  }
}

ribi::cmap::QtRateConceptTallyDialogNewName::~QtRateConceptTallyDialogNewName() noexcept
{
  delete ui;
}

std::vector<ribi::cmap::QtRateConceptTallyDialogNewName::Row>
  ribi::cmap::QtRateConceptTallyDialogNewName::CreateData(const boost::shared_ptr</* const */ ribi::cmap::ConceptMap> map)
{
  std::vector<Row> data;

  if (!map) return data;

  assert(map);
  assert(map->GetFocalNode());

  //Add the focal concept its examples (not its name: this cannot be judged)
  {
    const boost::shared_ptr<Concept> focal_concept = map->GetFocalNode()->GetConcept();
    assert(focal_concept);
    const int n_examples = boost::numeric_cast<int>(focal_concept->GetExamples()->Get().size());
    for (int i=0; i!=n_examples; ++i)
    {
      boost::shared_ptr<Edge> empty_edge;
      data.push_back(std::make_tuple(empty_edge,focal_concept,i));
    }
  }

  //Collect all relations of the focal node of this sub concept map
  for(const boost::shared_ptr<Edge> edge: map->GetEdges())
  {
    //But skip the connections to the focal question
    if (boost::dynamic_pointer_cast<cmap::CenterNode>(edge->GetTo())
      || boost::dynamic_pointer_cast<cmap::CenterNode>(edge->GetFrom()))
    {
      continue;
    }


    const boost::shared_ptr<Concept> concept = edge->GetNode()->GetConcept();
    data.push_back(std::make_tuple(edge,concept,-1));
    const int n_examples = boost::numeric_cast<int>(concept->GetExamples()->Get().size());
    for (int i=0; i!=n_examples; ++i)
    {
      boost::shared_ptr<Edge> empty_edge;
      data.push_back(std::make_tuple(empty_edge,concept,i));
    }
  }
  return data;
}

const boost::shared_ptr<ribi::cmap::ConceptMap> ribi::cmap::QtRateConceptTallyDialogNewName::CreateTestConceptMap()
{
  //Create a subconcept map for testing:
  // - node with a concept with (1) text 'TextNode' (2) one example with text 'TextExampleNode'
  // - edge with a concept with (1) text 'TextEdge' (2) one example with text 'TextExampleEdge'
  // - node with a concept with (1) text 'TextDontCare'

  const boost::shared_ptr<Concept> concept_node_focal(ConceptFactory().Create(
    "Node_focal: This is displayed at the top of the dialog and should have line wrapping",
    {
      {"Node_focal example",Competency::misc}
    },
    0,1,2));
  const boost::shared_ptr<Concept> concept_node_other(ConceptFactory().Create(
    "Node_other: Issue #206 is about line wrapping in this dialog. Before this issue was resolved, this long line trailed with a ... to indicate that there was even more text at the right",
    {
      { "Example that won't be displayed here",Competency::misc }
    },
    0,1,2));

  const boost::shared_ptr<Concept> concept_edge(ConceptFactory().Create(
    "Edge: Issue #206 is about line wrapping in this dialog. Before this issue was resolved, this long line trailed with a ... to indicate that there was even more text at the right",
    {
      {"Edge example",Competency::misc}
    },
    2,1,0));
  const boost::shared_ptr<Node> node_focal(NodeFactory().Create(concept_node_focal));
  const boost::shared_ptr<Node> node_other(NodeFactory().Create(concept_node_other));

  const boost::shared_ptr<ConceptMap> sub_concept_map(
    ConceptMapFactory().Create(
      {
        node_focal,
        node_other
      } ,
      {
        EdgeFactory().Create(
          NodeFactory().Create(concept_edge,1.2,3.4),node_focal,true,node_other,true
        )
      }
    )
  );
  assert(sub_concept_map);
  return sub_concept_map;
}

std::string ribi::cmap::QtRateConceptTallyDialogNewName::GetFocusName(
  const boost::shared_ptr<const ribi::cmap::ConceptMap> sub_concept_map) noexcept
{
  if (sub_concept_map)
  {
    assert(sub_concept_map->GetFocalNode());
    const boost::shared_ptr<const Concept> focal_concept {
      sub_concept_map->GetFocalNode()->GetConcept()
    };
    assert(focal_concept);
    return focal_concept->GetName();
  }
  else
  {
    return "(geen concept)";
  }
}

int ribi::cmap::QtRateConceptTallyDialogNewName::GetSuggestedComplexity() const
{
  //Tally the edges that contribute to complexity
  const int n_edges = std::accumulate(m_data.begin(),m_data.end(),0,
    [](int init, const Row& row)
      {
        return init + (std::get<2>(row) == -1 && std::get<1>(row)->GetIsComplex() ? 1 : 0);
      }
    );

  //Tally the examples that contribute to complexity
  const int n_examples = std::accumulate(m_data.begin(),m_data.end(),0,
    [](int init, const Row& row)
      {
        const int index = std::get<2>(row);
        if (index == -1) return init + 0;
        assert(std::get<1>(row));
        assert(std::get<1>(row)->GetExamples());
        assert(index < static_cast<int>(std::get<1>(row)->GetExamples()->Get().size()));
        return init + (std::get<1>(row)->GetExamples()->Get()[index]->GetIsComplex() ? 1 : 0);
      }
    );
  const int n_tallied = n_examples + n_edges;
  if (n_tallied < 2) return 0;
  if (n_tallied < 4) return 1;
  return 2;
}

int ribi::cmap::QtRateConceptTallyDialogNewName::GetSuggestedConcreteness() const
{
  //Tally the examples that contribute to concreteness
  const int n_examples = std::accumulate(m_data.begin(),m_data.end(),0,
    [](int init, const Row& row)
      {
        const int index = std::get<2>(row);
        if (index == -1) return init + 0;
        assert(std::get<1>(row));
        assert(std::get<1>(row)->GetExamples());
        assert(index < static_cast<int>(std::get<1>(row)->GetExamples()->Get().size()));
        return init + (std::get<1>(row)->GetExamples()->Get()[index]->GetIsConcrete() ? 1 : 0);
      }
    );
  const int n_tallied = n_examples;
  if (n_tallied < 2) return 0;
  if (n_tallied < 4) return 1;
  return 2;
}

int ribi::cmap::QtRateConceptTallyDialogNewName::GetSuggestedSpecificity() const
{
  //Tally the examples that contribute to specificity
  const int n_examples = std::accumulate(m_data.begin(),m_data.end(),0,
    [](int init, const Row& row)
      {
        const int index = std::get<2>(row);
        if (index == -1) return init + 0;
        assert(std::get<1>(row));
        assert(index < static_cast<int>(std::get<1>(row)->GetExamples()->Get().size()));
        assert(std::get<1>(row)->GetExamples());
        return init + (std::get<1>(row)->GetExamples()->Get()[index]->GetIsSpecific() ? 1 : 0);
      }
    );
  const int n_tallied = n_examples;
  if (n_tallied < 2) return 0;
  if (n_tallied < 4) return 1;
  return 2;
}

void ribi::cmap::QtRateConceptTallyDialogNewName::keyPressEvent(QKeyEvent * event)
{
  if (event->key() == Qt::Key_Escape) { close(); return; }
  if ( (event->modifiers() & Qt::ControlModifier)
    && (event->modifiers() & Qt::ShiftModifier)
    && event->key() == Qt::Key_T)
  {
    //Translate
    this->setWindowTitle("Relevance of illustrations");
    {
      ui->label_concept_name->setText(
        ("Illustrations and relations of the cluster: " + m_focus_name).c_str()
      );
    }
    {
      QTableWidgetItem * const item = new QTableWidgetItem;
      item->setText("Illustrations and relations of the cluster:");
      ui->table->setHorizontalHeaderItem(3,item);
    }
    {
      const int x = GetSuggestedComplexity();
      const int c = GetSuggestedConcreteness();
      const int s = GetSuggestedSpecificity();
      std::stringstream m;
      m << "Complexity: " << x << ", concreteness: " << c << ", specificity: " << s;
      ui->label_debug->setText(m.str().c_str());
    }
    return;
  }
}

void ribi::cmap::QtRateConceptTallyDialogNewName::OnCellChanged(int row_index, int col)
{
  assert(row_index >= 0);
  assert(row_index < static_cast<int>(m_data.size()));
  assert(col >= 0);
  assert(col < 4);
  const QTableWidgetItem * const item = ui->table->item(row_index,col);
  assert(item);
  const Row& row = m_data[row_index];
  boost::shared_ptr<Concept> concept = std::get<1>(row);
  const int index = std::get<2>(row);

  if (index == -1)
  {
    //Concept name
    switch (col)
    {
      case 0: concept->SetIsComplex( item->checkState() == Qt::Checked );
      case 1: break; //Empty cell
      case 2: break; //Empty cell
      case 3: break; //It's read-only! //concept->SetName( item->text().toStdString() ); break;
    }
  }
  else
  {
    //Concept example
    assert(index < static_cast<int>(concept->GetExamples()->Get().size()));
    const boost::shared_ptr<cmap::Example> example = concept->GetExamples()->Get()[index];
    switch (col)
    {
      case 0: example->SetIsComplex( item->checkState() == Qt::Checked ); break;
      case 1: example->SetIsConcrete( item->checkState() == Qt::Checked ); break;
      case 2: example->SetIsSpecific( item->checkState() == Qt::Checked ); break;
      case 3: break; //It's read-only! //example->SetText( item->text().toStdString() ); break;
    }
  }

  {
    const int x = GetSuggestedComplexity();
    const int c = GetSuggestedConcreteness();
    const int s = GetSuggestedSpecificity();
    std::stringstream m;
    m << "Complexiteit: " << x << ", concreetheid: " << c << ", specificiteit: " << s;
    ui->label_debug->setText(m.str().c_str());
  }
}

void ribi::cmap::QtRateConceptTallyDialogNewName::resizeEvent(QResizeEvent *)
{
  const int small_col_width = 28;
  ui->table->setColumnWidth(0, small_col_width);
  ui->table->setColumnWidth(1, small_col_width);
  ui->table->setColumnWidth(2, small_col_width);
  const int extra_space = 8;
  ui->table->setColumnWidth(3,ui->table->width() - (3 * small_col_width) - (3 * extra_space));
}

void ribi::cmap::QtRateConceptTallyDialogNewName::on_button_ok_clicked()
{
  close();
}

#ifndef NDEBUG
void ribi::cmap::QtRateConceptTallyDialogNewName::Test() noexcept
{
  {
    static bool is_tested = false;
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer{__func__,__FILE__,0.1};
  //Empty table
  {
    const boost::shared_ptr<ConceptMap> concept_map;
    assert(!concept_map);
    QtRateConceptTallyDialogNewName d(concept_map);
  }

  const boost::shared_ptr<ConceptMap> concept_map = CreateTestConceptMap();
  assert(concept_map);


  QtRateConceptTallyDialogNewName d(concept_map);

  #ifndef NDEBUG
  {
    if(d.ui->table->columnCount() != 4) TRACE(d.ui->table->columnCount());
    if(d.ui->table->rowCount() != 3) TRACE(d.ui->table->rowCount());
  }
  #endif

  assert(d.ui->table->columnCount() == 4);
  assert(d.ui->table->rowCount() == 3);
  assert(concept_map->GetNodes().size() == 2);
  assert(concept_map->GetEdges().size() == 1);
  const boost::shared_ptr<Node> focal_node = concept_map->GetFocalNode();
  //const boost::shared_ptr<Node> other_node = concept_map->GetNodes()[1]; //Don't care
  const boost::shared_ptr<Edge> edge = concept_map->GetEdges()[0];

  assert(d.ui->table->item(0,0)->flags() == (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable));
  assert(d.ui->table->item(0,1)->flags() == (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable));
  assert(d.ui->table->item(0,2)->flags() == (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable));
  assert(d.ui->table->item(0,3)->flags() == (Qt::ItemIsSelectable | Qt::ItemIsEnabled));

  assert(d.ui->table->item(1,0)->flags() == (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable));
  assert(d.ui->table->item(1,1)->flags() == Qt::ItemIsEnabled); //Empty
  assert(d.ui->table->item(1,2)->flags() == Qt::ItemIsEnabled); //Empty
  assert(d.ui->table->item(1,3)->flags() == (Qt::ItemIsSelectable | Qt::ItemIsEnabled));

  assert(d.ui->table->item(2,0)->flags() == (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable));
  assert(d.ui->table->item(2,1)->flags() == (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable));
  assert(d.ui->table->item(2,2)->flags() == (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable));
  assert(d.ui->table->item(2,3)->flags() == (Qt::ItemIsSelectable | Qt::ItemIsEnabled));

  //Check current state, before modification

  assert(d.ui->table->item(0,0)->checkState() == (focal_node->GetConcept()->GetExamples()->Get()[0]->GetIsComplex() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(0,1)->checkState() == (focal_node->GetConcept()->GetExamples()->Get()[0]->GetIsConcrete() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(0,2)->checkState() == (focal_node->GetConcept()->GetExamples()->Get()[0]->GetIsSpecific() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(0,3)->text() == QString(focal_node->GetConcept()->GetExamples()->Get()[0]->GetText().c_str()));

  assert(d.ui->table->item(1,0)->checkState() == (edge->GetNode()->GetConcept()->GetIsComplex() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(1,1)->text() == "");
  assert(d.ui->table->item(1,2)->text() == "");
  //NEW 20131231: now the text contains both
  //- the concept name of the edge
  //- the name of the node the edge is connected to
  assert(d.ui->table->item(1,3)->text().toStdString().find(edge->GetNode()->GetConcept()->GetName()) != std::string::npos);
  assert(d.ui->table->item(1,3)->text().toStdString().find(edge->GetTo()->GetConcept()->GetName()) != std::string::npos);
  //OLD assert(d.ui->table->item(1,3)->text() == QString(edge->GetConcept()->GetName().c_str()));

  assert(d.ui->table->item(2,0)->checkState() == (edge->GetNode()->GetConcept()->GetExamples()->Get()[0]->GetIsComplex() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(2,1)->checkState() == (edge->GetNode()->GetConcept()->GetExamples()->Get()[0]->GetIsConcrete() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(2,2)->checkState() == (edge->GetNode()->GetConcept()->GetExamples()->Get()[0]->GetIsSpecific() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(2,3)->text() == QString(edge->GetNode()->GetConcept()->GetExamples()->Get()[0]->GetText().c_str()));

  //Modify table
  d.ui->table->item(0,0)->setCheckState(d.ui->table->item(0,0)->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked);
  d.ui->table->item(0,1)->setCheckState(d.ui->table->item(0,1)->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked);
  d.ui->table->item(0,2)->setCheckState(d.ui->table->item(0,2)->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked);
  //d.ui->table->item(0,3)->setText("MODIFIED"); //User should not be able to modify this

  d.ui->table->item(1,0)->setCheckState(d.ui->table->item(1,0)->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked);
  //d.ui->table->item(1,3)->setText("MODIFIED TOO"); //User should not be able to modify this

  d.ui->table->item(2,0)->setCheckState(d.ui->table->item(2,0)->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked);
  d.ui->table->item(2,1)->setCheckState(d.ui->table->item(2,1)->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked);
  d.ui->table->item(2,2)->setCheckState(d.ui->table->item(2,2)->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked);
  //d.ui->table->item(2,3)->setText("MODIFIED AS WELL"); //User should not be able to modify this

  //Check that data is modified by GUI

  assert(d.ui->table->item(0,0)->checkState() == (focal_node->GetConcept()->GetExamples()->Get()[0]->GetIsComplex() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(0,1)->checkState() == (focal_node->GetConcept()->GetExamples()->Get()[0]->GetIsConcrete() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(0,2)->checkState() == (focal_node->GetConcept()->GetExamples()->Get()[0]->GetIsSpecific() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(0,3)->text() == QString(focal_node->GetConcept()->GetExamples()->Get()[0]->GetText().c_str()));

  assert(d.ui->table->item(1,0)->checkState() == (edge->GetNode()->GetConcept()->GetIsComplex() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(1,1)->text() == "");
  assert(d.ui->table->item(1,2)->text() == "");

  //NEW 20131231: now the text contains both
  //- the concept name of the edge
  //- the name of the node the edge is connected to
  assert(d.ui->table->item(1,3)->text().toStdString().find(edge->GetNode()->GetConcept()->GetName()) != std::string::npos);
  assert(d.ui->table->item(1,3)->text().toStdString().find(edge->GetTo()->GetConcept()->GetName()) != std::string::npos);
  //OLD assert(d.ui->table->item(1,3)->text() == QString(edge->GetConcept()->GetName().c_str()));

  assert(d.ui->table->item(2,0)->checkState() == (edge->GetNode()->GetConcept()->GetExamples()->Get()[0]->GetIsComplex() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(2,1)->checkState() == (edge->GetNode()->GetConcept()->GetExamples()->Get()[0]->GetIsConcrete() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(2,2)->checkState() == (edge->GetNode()->GetConcept()->GetExamples()->Get()[0]->GetIsSpecific() ? Qt::Checked : Qt::Unchecked));
  assert(d.ui->table->item(2,3)->text() == QString(edge->GetNode()->GetConcept()->GetExamples()->Get()[0]->GetText().c_str()));

}
#endif

 

 

 

 

 

./CppQtConceptMap/qtconceptmapratedconceptdialog.h

 

//---------------------------------------------------------------------------
/*
QtConceptMap, Qt classes for display and interaction with ConceptMap
Copyright (C) 2013-2015 The Brainweaver Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtConceptMap.htm
//---------------------------------------------------------------------------
#ifndef QTCONCEPTMAPRATEDCONCEPTDIALOG_H
#define QTCONCEPTMAPRATEDCONCEPTDIALOG_H


#include <string>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#include <boost/shared_ptr.hpp>
#include <QDialog>
#include "qtconceptmapfwd.h"
#pragma GCC diagnostic pop

namespace Ui { class QtConceptMapRatedConceptDialog; }

namespace ribi {
namespace cmap {

class QtConceptMapRatedConceptDialog : public QDialog
{
  Q_OBJECT
  
public:

  explicit QtConceptMapRatedConceptDialog(
    const boost::shared_ptr<const ConceptMap> concept_map,
    const boost::shared_ptr<const cmap::Node> node,
    QWidget *parent = 0);
  QtConceptMapRatedConceptDialog(const QtConceptMapRatedConceptDialog&) = delete;
&