Переглянути джерело

Extract logic from ctkCheckableHeaderView

Move the logic into ctkCheckableModelHelper to be reused by
other widgets if needed.
Julien Finet 15 роки тому
батько
коміт
8b41d0748c

+ 3 - 0
Libs/Core/CMakeLists.txt

@@ -33,6 +33,8 @@ SET(KIT_SRCS
   ctkAbstractLibraryFactory.tpp
   ctkCallback.cpp
   ctkCallback.h
+  ctkCheckableModelHelper.cpp
+  ctkCheckableModelHelper.h
   ctkCommandLineParser.cpp
   ctkCommandLineParser.h
   ctkDependencyGraph.cpp
@@ -90,6 +92,7 @@ ENDIF()
 # Headers that should run through moc
 SET(KIT_MOC_SRCS
   ctkCallback.h
+  ctkCheckableModelHelper.h
   ctkCommandLineParser.h
   ctkErrorLogFDMessageHandler_p.h
   ctkErrorLogModel.h

+ 2 - 0
Libs/Core/Testing/Cpp/CMakeLists.txt

@@ -26,6 +26,7 @@ SET(KITTests_SRCS
   ctkAbstractPluginFactoryTest1.cpp
   ctkAbstractQObjectFactoryTest1.cpp
   ctkCallbackTest1.cpp
+  ctkCheckableModelHelperTest1.cpp
   ctkCommandLineParserTest1.cpp
   ctkErrorLogModelTest1.cpp
   ctkErrorLogModelTest2.cpp
@@ -112,6 +113,7 @@ IF(HAVE_BFD)
   SET_PROPERTY(TEST ctkBinaryFileDescriptorTest1 PROPERTY LABELS ${PROJECT_NAME})
 ENDIF()
 SIMPLE_TEST( ctkCallbackTest1 )
+SIMPLE_TEST( ctkCheckableModelHelperTest1 )
 SIMPLE_TEST( ctkCommandLineParserTest1 )
 SIMPLE_TEST( ctkDependencyGraphTest1 )
 SIMPLE_TEST( ctkDependencyGraphTest2 )

+ 258 - 0
Libs/Core/Testing/Cpp/ctkCheckableModelHelperTest1.cpp

@@ -0,0 +1,258 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QDebug>
+#include <QApplication>
+#include <QFocusEvent>
+#include <QTableView>
+#include <QStandardItem>
+#include <QStandardItemModel>
+#include <QTimer>
+
+// CTK includes
+#include "ctkCheckableModelHelper.h"
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+//-----------------------------------------------------------------------------
+int ctkCheckableModelHelperTest1(int argc, char * argv [] )
+{
+  QApplication app(argc, argv);
+
+  QStandardItemModel model;
+  QList<QStandardItem*> row0;
+  row0 << new QStandardItem << new QStandardItem << new QStandardItem;
+  row0[0]->setText("not user checkable");
+  model.appendRow(row0);
+  QList<QStandardItem*> row1;
+  row1 << new QStandardItem << new QStandardItem << new QStandardItem;
+  row1[0]->setCheckable(true);
+  row1[0]->setText("checkable");
+  model.appendRow(row1);
+  QList<QStandardItem*> row2;
+  row2 << new QStandardItem << new QStandardItem << new QStandardItem;
+  row2[0]->setCheckable(true);
+  row2[0]->setText("checkable");
+  model.appendRow(row2);
+
+  // items are unchecked by default 
+  if (row0[0]->checkState() != Qt::Unchecked ||
+      row1[0]->checkState() != Qt::Unchecked ||
+      row2[0]->checkState() != Qt::Unchecked)
+    {
+    std::cerr << "QStandardItem default failed: "
+	      << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  // Header is checked by default
+  model.setHeaderData(0, Qt::Horizontal, Qt::Checked, Qt::CheckStateRole);
+
+  ctkCheckableModelHelper* modelHelper =
+    new ctkCheckableModelHelper(Qt::Horizontal);
+  modelHelper->setModel(&model);
+  // propagatetoitems is true by default
+  //modelHelper->setPropagateToItems(true);
+
+  // As propagateToItems is true, once the model is set to the modelHelper,
+  // the checkable header is updated from the check state of all the items
+  // all the items are unchecked by default, so the header becomes unchecked
+  if (modelHelper->headerCheckState(0) != Qt::Unchecked ||
+      row0[0]->checkState() != Qt::Unchecked ||
+      row1[0]->checkState() != Qt::Unchecked ||
+      row2[0]->checkState() != Qt::Unchecked)
+    {
+    std::cerr << "ctkCheckableModelHelper::checkstate() failed: "
+              << static_cast<int>(modelHelper->headerCheckState(0)) << " "
+	      << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+  // Retrieve checkstate of the header
+  Qt::CheckState checkstate;
+  if (!modelHelper->headerCheckState(0, checkstate))
+    {
+    std::cerr << "ctkCheckableModelHelper::checkstate() failed: "
+              << static_cast<int>(checkstate) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  if (modelHelper->propagateDepth() == 0)
+    {
+    std::cerr << "ctkCheckableModelHelper::propagateDepth() failed: "
+              << modelHelper->propagateDepth() << std::endl;
+    return EXIT_FAILURE;
+    }
+  modelHelper->setPropagateDepth(0);
+  if (modelHelper->propagateDepth() != 0)
+    {
+    std::cerr << "ctkCheckableModelHelper::propagateDepth() failed: "
+              << modelHelper->propagateDepth() << std::endl;
+    return EXIT_FAILURE;
+    }
+  if (modelHelper->headerCheckState(0) != Qt::Unchecked ||
+      row0[0]->checkState() != Qt::Unchecked ||
+      row1[0]->checkState() != Qt::Unchecked ||
+      row2[0]->checkState() != Qt::Unchecked)
+    {
+    std::cerr << "ctkCheckableModelHelper::propagateToItems() failed: "
+              << static_cast<int>(modelHelper->headerCheckState(0)) << " "
+	      << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  // check the header
+  modelHelper->setHeaderCheckState(0, Qt::Checked);
+  
+  // make sure it didn't uncheck the checkable items
+  if (modelHelper->headerCheckState(0) != Qt::Checked ||
+      row0[0]->checkState() != Qt::Unchecked ||
+      row1[0]->checkState() != Qt::Unchecked ||
+      row2[0]->checkState() != Qt::Unchecked)
+    {
+    std::cerr << __LINE__ << " ctkCheckableModelHelper::toggleCheckState() failed: "
+              << static_cast<int>(modelHelper->headerCheckState(0)) << " "
+	      << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  row0[0]->setCheckState(Qt::Checked);
+  // make sure it didn't uncheck the checkable items
+  if (modelHelper->headerCheckState(0) != Qt::Checked ||
+      row0[0]->checkState() != Qt::Checked ||
+      row1[0]->checkState() != Qt::Unchecked ||
+      row2[0]->checkState() != Qt::Unchecked)
+    {
+    std::cerr << "QStandardItem::setCheckState() failed: "
+              << static_cast<int>(modelHelper->headerCheckState(0)) << " "
+              << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  // The checkable header gets updated with the item check states
+  modelHelper->setPropagateDepth(-1);
+
+  if (modelHelper->propagateDepth() == 0 ||
+      modelHelper->headerCheckState(0) != Qt::PartiallyChecked ||
+      row0[0]->checkState() != Qt::Checked ||
+      row1[0]->checkState() != Qt::Unchecked ||
+      row2[0]->checkState() != Qt::Unchecked)
+    {
+    std::cerr << "ctkCheckableModelHelper::setPropagateToItems() failed: "
+              << static_cast<int>(modelHelper->headerCheckState(0)) << " "
+              << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  row0[0]->setCheckState(Qt::Unchecked);
+  
+  if (modelHelper->headerCheckState(0) != Qt::Unchecked ||
+      row0[0]->checkState() != Qt::Unchecked ||
+      row1[0]->checkState() != Qt::Unchecked ||
+      row2[0]->checkState() != Qt::Unchecked)
+    {
+    std::cerr << "QStandardItem::setCheckState() failed: "
+              << static_cast<int>(modelHelper->headerCheckState(0)) << " "
+              << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  row1[0]->setCheckState(Qt::Checked);
+  
+    // make sure it didn't uncheck the checkable items
+  if (modelHelper->headerCheckState(0) != Qt::PartiallyChecked ||
+      row0[0]->checkState() != Qt::Unchecked ||
+      row1[0]->checkState() != Qt::Checked ||
+      row2[0]->checkState() != Qt::Unchecked)
+    {
+    std::cerr << "QStandardItem::setCheckState() failed: "
+              << static_cast<int>(modelHelper->headerCheckState(0)) << " "
+              << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  row1[0]->setCheckState(Qt::Checked);
+  
+  // make sure it didn't check the checkable items
+  if (modelHelper->headerCheckState(0) != Qt::PartiallyChecked ||
+      row0[0]->checkState() != Qt::Unchecked ||
+      row1[0]->checkState() != Qt::Checked ||
+      row2[0]->checkState() != Qt::Unchecked)
+    {
+    std::cerr << "QStandardItem::setCheckState() failed: "
+              << static_cast<int>(modelHelper->headerCheckState(0)) << " "
+              << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  row0[0]->setCheckState(Qt::Checked);
+  row2[0]->setCheckState(Qt::Checked);
+
+  // make sure the header is now checked
+  if (modelHelper->headerCheckState(0) != Qt::Checked ||
+      row0[0]->checkState() != Qt::Checked ||
+      row1[0]->checkState() != Qt::Checked ||
+      row2[0]->checkState() != Qt::Checked)
+    {
+    std::cerr << "QStandardItem::setCheckState() failed: "
+              << static_cast<int>(modelHelper->headerCheckState(0)) << " "
+              << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  modelHelper->setHeaderCheckState(0, Qt::Unchecked);
+    
+  if (modelHelper->headerCheckState(0) != Qt::Unchecked ||
+      row0[0]->checkState() != Qt::Unchecked ||
+      row1[0]->checkState() != Qt::Unchecked ||
+      row2[0]->checkState() != Qt::Unchecked)
+    {
+    std::cerr << "ctkCheckableModelHelper::setCheckState() failed: "
+              << static_cast<int>(modelHelper->headerCheckState(0)) << " "
+              << static_cast<int>(row0[0]->checkState()) << " "
+              << static_cast<int>(row1[0]->checkState()) << " "
+              << static_cast<int>(row2[0]->checkState()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  return EXIT_SUCCESS;
+}

+ 649 - 0
Libs/Core/ctkCheckableModelHelper.cpp

@@ -0,0 +1,649 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QAbstractItemModel>
+#include <QApplication>
+#include <QDebug>
+#include <QStandardItemModel>
+
+// CTK includes
+#include "ctkCheckableModelHelper.h"
+
+//-----------------------------------------------------------------------------
+class ctkCheckableModelHelperPrivate
+{
+  Q_DECLARE_PUBLIC(ctkCheckableModelHelper);
+protected:
+  ctkCheckableModelHelper* const q_ptr;
+public:
+  ctkCheckableModelHelperPrivate(ctkCheckableModelHelper& object);
+  ~ctkCheckableModelHelperPrivate();
+
+  void init();
+  /// Set index checkstate and call propagate
+  void setIndexCheckState(const QModelIndex& index, Qt::CheckState checkState);
+  /// Return the depth in the model tree of the index.
+  /// -1 if the index is the root element a header or a header, 0 if the index
+  /// is a toplevel index, 1 if its parent is toplevel, 2 if its grandparent is
+  /// toplevel, etc.
+  int indexDepth(const QModelIndex& modelIndex)const;
+  /// Set the checkstate of the index based on its children and grand children
+  void updateCheckState(const QModelIndex& modelIndex);
+  /// Set the check state of the index to all its children and grand children
+  void propagateCheckStateToChildren(const QModelIndex& modelIndex);
+
+  Qt::CheckState checkState(const QModelIndex& index, bool *checkable)const;
+  void setCheckState(const QModelIndex& index, Qt::CheckState newCheckState);
+
+  void forceCheckability(const QModelIndex& index);
+
+  QAbstractItemModel* Model;
+  QModelIndex         RootIndex;
+  Qt::Orientation     Orientation;
+  bool                HeaderIsUpdating;
+  bool                ItemsAreUpdating;
+  bool                ForceCheckability;
+  /// 0 means no propagation
+  /// -1 means unlimited propagation
+  /// 1 means propagate to top-level indexes
+  /// 2 means propagate to top-level and their children
+  /// ...
+  int                 PropagateDepth;
+  Qt::CheckState      DefaultCheckState;
+};
+
+//----------------------------------------------------------------------------
+ctkCheckableModelHelperPrivate::ctkCheckableModelHelperPrivate(ctkCheckableModelHelper& object)
+  : q_ptr(&object)
+{
+  this->Model = 0;
+  this->HeaderIsUpdating = false;
+  this->ItemsAreUpdating = false;
+  this->ForceCheckability = false;
+  this->PropagateDepth = -1;
+  this->DefaultCheckState = Qt::Unchecked;
+}
+
+//-----------------------------------------------------------------------------
+ctkCheckableModelHelperPrivate::~ctkCheckableModelHelperPrivate()
+{
+}
+
+//----------------------------------------------------------------------------
+void ctkCheckableModelHelperPrivate::init()
+{
+  Q_Q(ctkCheckableModelHelper);
+}
+
+//----------------------------------------------------------------------------
+Qt::CheckState ctkCheckableModelHelperPrivate::checkState(
+  const QModelIndex& index, bool *checkable)const
+{
+  Q_Q(const ctkCheckableModelHelper);
+  QVariant indexCheckState = index != q->rootIndex() ?
+    q->model()->data(index, Qt::CheckStateRole):
+    q->model()->headerData(0, q->orientation(), Qt::CheckStateRole);
+  return static_cast<Qt::CheckState>(indexCheckState.toInt(checkable));
+}
+
+//----------------------------------------------------------------------------
+void ctkCheckableModelHelperPrivate::setCheckState(
+  const QModelIndex& modelIndex, Qt::CheckState newCheckState)
+{
+  Q_Q(ctkCheckableModelHelper);
+  if (modelIndex != q->rootIndex())
+    {
+    q->model()->setData(modelIndex, newCheckState, Qt::CheckStateRole);
+    }
+  else
+    {
+    q->model()->setHeaderData(0, q->orientation(), newCheckState, Qt::CheckStateRole);
+    }
+}
+
+//----------------------------------------------------------------------------
+void ctkCheckableModelHelperPrivate::setIndexCheckState(
+  const QModelIndex& index, Qt::CheckState checkState)
+{
+  Q_Q(ctkCheckableModelHelper);
+  bool checkable = false;
+  this->checkState(index, &checkable);
+  if (!checkable && !this->ForceCheckability)
+    {
+    // The index is not checkable and we don't want to force checkability
+    return;
+    }
+  this->setCheckState(index, checkState);
+  this->propagateCheckStateToChildren(index);
+}
+
+//-----------------------------------------------------------------------------
+int ctkCheckableModelHelperPrivate::indexDepth(const QModelIndex& modelIndex)const
+{
+  int depth = -1;
+  QModelIndex parentIndex = modelIndex;
+  while (parentIndex.isValid())
+    {
+    ++depth;
+    parentIndex = parentIndex.parent();
+    }
+  return depth;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelperPrivate
+::updateCheckState(const QModelIndex& modelIndex)
+{
+  Q_Q(ctkCheckableModelHelper);
+  bool checkable = false;
+  int oldCheckState = this->checkState(modelIndex, &checkable);
+  if (!checkable)
+    {
+    return;
+    }
+
+  Qt::CheckState newCheckState = Qt::PartiallyChecked;
+  bool firstCheckableChild = true;
+  const int rowCount = q->orientation() == Qt::Horizontal ?
+    q->model()->rowCount(modelIndex) : 1;
+  const int columnCount = q->orientation() == Qt::Vertical ?
+    q->model()->columnCount(modelIndex) : 1;
+  for (int r = 0; r < rowCount; ++r)
+    {
+    for (int c = 0; c < columnCount; ++c)
+      {
+      QModelIndex child = q->model()->index(r, c, modelIndex);
+      QVariant childCheckState = q->model()->data(child, Qt::CheckStateRole);
+      int childState = childCheckState.toInt(&checkable);
+      if (!checkable)
+        {
+        continue;
+        }
+      if (firstCheckableChild)
+        {
+        newCheckState = static_cast<Qt::CheckState>(childState);
+        firstCheckableChild = false;
+        }
+      if (newCheckState != childState)
+        {
+        newCheckState = Qt::PartiallyChecked;
+        }
+      if (newCheckState == Qt::PartiallyChecked)
+        {
+        break;
+        }
+      }
+    if (!firstCheckableChild && newCheckState == Qt::PartiallyChecked)
+      {
+      break;
+      }
+    }
+  if (oldCheckState == newCheckState)
+    {
+    return;
+    }
+  this->setCheckState(modelIndex, newCheckState);
+  if (modelIndex != q->rootIndex())
+    {
+    this->updateCheckState(modelIndex.parent());
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelperPrivate
+::propagateCheckStateToChildren(const QModelIndex& modelIndex)
+{
+  Q_Q(ctkCheckableModelHelper);
+  int indexDepth = this->indexDepth(modelIndex);
+  if (this->PropagateDepth == 0 ||
+      !(indexDepth < this->PropagateDepth || this->PropagateDepth == -1))
+    {
+    return;
+    }
+
+  bool checkable = false;
+  Qt::CheckState checkState = this->checkState(modelIndex, &checkable);
+  if (!checkable || checkState == Qt::PartiallyChecked)
+    {
+    return;
+    }
+
+  while (this->ForceCheckability && q->model()->canFetchMore(modelIndex))
+    {
+    q->model()->fetchMore(modelIndex);
+    }
+  
+  const int rowCount = q->orientation() == Qt::Horizontal ?
+    q->model()->rowCount(modelIndex) : 1;
+  const int columnCount = q->orientation() == Qt::Vertical ?
+    q->model()->columnCount(modelIndex) : 1;
+  for (int r = 0; r < rowCount; ++r)
+    {
+    for (int c = 0; c < columnCount; ++c)
+      {
+      QModelIndex child = q->model()->index(r, c, modelIndex);
+      this->setIndexCheckState(child, checkState);
+      }
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelperPrivate
+::forceCheckability(const QModelIndex& modelIndex)
+{
+  Q_Q(ctkCheckableModelHelper);
+  if (!this->ForceCheckability)
+    {
+    return;
+    }
+  this->setCheckState(modelIndex, this->DefaultCheckState);
+  if (qobject_cast<QStandardItemModel*>(q->model()))
+    {
+    QStandardItem* item = modelIndex != q->rootIndex() ?
+      qobject_cast<QStandardItemModel*>(q->model())->itemFromIndex(modelIndex) :
+      (q->orientation() == Qt::Horizontal ?
+         qobject_cast<QStandardItemModel*>(q->model())->horizontalHeaderItem(0) :
+         qobject_cast<QStandardItemModel*>(q->model())->verticalHeaderItem(0));
+    item->setCheckable(true);
+    }
+}
+
+//----------------------------------------------------------------------------
+ctkCheckableModelHelper::ctkCheckableModelHelper(
+  Qt::Orientation orient, QObject* objectParent)
+  : QObject(objectParent)
+  , d_ptr(new ctkCheckableModelHelperPrivate(*this))
+{
+  Q_D(ctkCheckableModelHelper);
+  d->Orientation = orient;
+  d->init();
+}
+
+//-----------------------------------------------------------------------------
+ctkCheckableModelHelper::~ctkCheckableModelHelper()
+{
+}
+
+//-----------------------------------------------------------------------------
+Qt::Orientation ctkCheckableModelHelper::orientation()const
+{
+  Q_D(const ctkCheckableModelHelper);
+  return d->Orientation;
+}
+
+//-----------------------------------------------------------------------------
+QAbstractItemModel* ctkCheckableModelHelper::model()const
+{
+  Q_D(const ctkCheckableModelHelper);
+  return d->Model;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::setModel(QAbstractItemModel *newModel)
+{
+  Q_D(ctkCheckableModelHelper);
+  QAbstractItemModel *current = this->model();
+  if (current == newModel)
+    {
+    return;
+    }
+  if(current)
+    {
+    this->disconnect(
+      current, SIGNAL(headerDataChanged(Qt::Orientation, int, int)),
+      this, SLOT(onHeaderDataChanged(Qt::Orientation, int, int)));
+    this->disconnect(
+      current, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
+      this, SLOT(onDataChanged(const QModelIndex&, const QModelIndex&)));
+    this->disconnect(
+      current, SIGNAL(columnsInserted(const QModelIndex &, int, int)), 
+      this, SLOT(onColumnsInserted(const QModelIndex &, int, int)));
+    this->disconnect(
+      current, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+      this, SLOT(onRowsInserted(const QModelIndex &, int, int)));
+    }
+  d->Model = newModel;
+  if(newModel)
+    {
+    this->connect(
+      newModel, SIGNAL(headerDataChanged(Qt::Orientation, int, int)),
+      this, SLOT(onHeaderDataChanged(Qt::Orientation, int, int)));
+    if (d->PropagateDepth != 0)
+      {
+      this->connect(
+        newModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
+        this, SLOT(onDataChanged(const QModelIndex&, const QModelIndex&)));
+      }
+    this->connect(
+      newModel, SIGNAL(columnsInserted(const QModelIndex &, int, int)),
+      this, SLOT(onColumnsInserted(const QModelIndex &, int, int)));
+    this->connect(
+      newModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+      this, SLOT(onRowsInserted(const QModelIndex &, int, int)));
+
+    if (d->ForceCheckability)
+      {
+      foreach(QModelIndex index, newModel->match(newModel->index(0,0), Qt::CheckStateRole, QVariant(), -1,Qt::MatchRecursive))
+        {
+        d->forceCheckability(index);
+        }
+      d->forceCheckability(this->rootIndex());
+      }
+    this->updateHeadersFromItems();
+    }
+}
+
+//-----------------------------------------------------------------------------
+QModelIndex ctkCheckableModelHelper::rootIndex()const
+{
+  Q_D(const ctkCheckableModelHelper);
+  return d->RootIndex;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::setRootIndex(const QModelIndex &index)
+{
+  Q_D(ctkCheckableModelHelper);
+  d->RootIndex = index;
+  if (d->PropagateDepth != 0)
+    {
+    this->updateHeadersFromItems();
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::setPropagateDepth(int depth)
+{
+  Q_D(ctkCheckableModelHelper);
+  if (d->PropagateDepth == depth)
+    {
+    return;
+    }
+  d->PropagateDepth = depth;
+  if (!this->model())
+    {
+    return;
+    }
+  if (depth != 0)
+    {
+    this->connect(
+      this->model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
+      this, SLOT(onDataChanged(const QModelIndex&, const QModelIndex&)), Qt::UniqueConnection);
+    this->updateHeadersFromItems();
+    }
+  else
+    {
+    this->disconnect(
+      this->model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
+      this, SLOT(onDataChanged(const QModelIndex&, const QModelIndex&)));
+    }
+}
+
+//-----------------------------------------------------------------------------
+int ctkCheckableModelHelper::propagateDepth()const
+{
+  Q_D(const ctkCheckableModelHelper);
+  return d->PropagateDepth;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::setForceCheckability(bool force)
+{
+  Q_D(ctkCheckableModelHelper);
+  if (d->ForceCheckability == force)
+    {
+    return;
+    }
+  d->ForceCheckability = force;
+  if (this->model())
+    {
+    d->propagateCheckStateToChildren(this->rootIndex());
+    }
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCheckableModelHelper::forceCheckability()const
+{
+  Q_D(const ctkCheckableModelHelper);
+  return d->ForceCheckability;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::setDefaultCheckState(Qt::CheckState default)
+{
+  Q_D(ctkCheckableModelHelper);
+  d->DefaultCheckState = default;
+}
+
+//-----------------------------------------------------------------------------
+Qt::CheckState ctkCheckableModelHelper::defaultCheckState()const
+{
+  Q_D(const ctkCheckableModelHelper);
+  return d->DefaultCheckState;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::setHeaderCheckState(int section, Qt::CheckState checkState)
+{
+  Q_D(ctkCheckableModelHelper);
+  QAbstractItemModel *current = this->model();
+  if(current == 0)
+    {    
+    return;
+    }
+  current->setHeaderData(section, this->orientation(),
+                         checkState, Qt::CheckStateRole);
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::setCheckState(const QModelIndex& index, Qt::CheckState checkState)
+{
+  Q_D(ctkCheckableModelHelper);
+  QAbstractItemModel *current = this->model();
+  if(current == 0)
+    {    
+    return;
+    }
+  current->setData(index, checkState, Qt::CheckStateRole);
+}
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::toggleCheckState(const QModelIndex& modelIndex)
+{
+  Q_D(ctkCheckableModelHelper);
+  // If the section is checkable, toggle the check state.
+  if(!this->isCheckable(modelIndex))
+    {
+    return;
+    }
+  // I've no strong feeling to turn the state checked or unchecked when the 
+  // state is PartiallyChecked.
+  this->setCheckState(modelIndex,
+    this->checkState(modelIndex) == Qt::Checked ? Qt::Unchecked : Qt::Checked);
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::toggleHeaderCheckState(int section)
+{
+  Q_D(ctkCheckableModelHelper);
+  // If the section is checkable, toggle the check state.
+  if(!this->isHeaderCheckable(section))
+    {
+    return;
+    }
+  // I've no strong feeling to turn the state checked or unchecked when the 
+  // state is PartiallyChecked.
+  this->setHeaderCheckState(section,
+    this->headerCheckState(section) == Qt::Checked ? Qt::Unchecked : Qt::Checked);
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::onHeaderDataChanged(Qt::Orientation orient,
+                                              int firstSection,
+                                              int lastSection)
+{
+  Q_D(ctkCheckableModelHelper);
+  if(orient != this->orientation())
+    {
+    return;
+    }
+  bool oldItemsAreUpdating = d->ItemsAreUpdating;
+  if (!d->ItemsAreUpdating)
+    {
+    d->ItemsAreUpdating = true;
+    d->propagateCheckStateToChildren(this->rootIndex());
+    }
+  d->ItemsAreUpdating = oldItemsAreUpdating;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::updateHeadersFromItems()
+{
+  Q_D(ctkCheckableModelHelper);
+  QAbstractItemModel *currentModel = this->model();
+  if (!currentModel)
+    {
+    return;
+    }
+  d->updateCheckState(QModelIndex());
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::onDataChanged(const QModelIndex & topLeft,
+                                           const QModelIndex & bottomRight)
+{
+  Q_UNUSED(bottomRight);
+  Q_D(ctkCheckableModelHelper);
+  if(d->ItemsAreUpdating || d->PropagateDepth == 0)
+    {
+    return;
+    }
+  bool checkable = false;
+  d->checkState(topLeft, &checkable);
+  if (!checkable)
+    {
+    return;
+    }
+  d->ItemsAreUpdating = true;
+  // TODO: handle topLeft "TO bottomRight"
+  d->propagateCheckStateToChildren(topLeft);
+  d->updateCheckState(topLeft.parent());
+
+  d->ItemsAreUpdating = false;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::onColumnsInserted(const QModelIndex &parentIndex,
+  int start, int end)
+{
+  Q_D(ctkCheckableModelHelper);
+  if (this->orientation() == Qt::Horizontal)
+    {
+    if (start == 0)
+      {
+      this->updateHeadersFromItems();
+      }
+    }
+  else
+    {
+    if (d->ForceCheckability)
+      {
+      for (int i = start; i <= end; ++i)
+        {
+        QModelIndex index = this->model()->index(0, i, parentIndex); 
+        d->forceCheckability(index);
+        }
+      }
+    this->onDataChanged(this->model()->index(0, start, parentIndex), 
+                        this->model()->index(0, end, parentIndex));
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableModelHelper::onRowsInserted(const QModelIndex &parentIndex,
+  int start, int end)
+{
+  Q_D(ctkCheckableModelHelper);
+  if (this->orientation() == Qt::Vertical)
+    {
+    if (start == 0)
+      {
+      this->updateHeadersFromItems();
+      }
+    }
+  else
+    {
+    if (d->ForceCheckability)
+      {
+      for (int i = start; i <= end; ++i)
+        {
+        QModelIndex index = this->model()->index(i, 0, parentIndex); 
+        d->forceCheckability(index);
+        }
+      }
+    this->onDataChanged(this->model()->index(start, 0, parentIndex), 
+                        this->model()->index(end, 0, parentIndex));
+    }
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCheckableModelHelper::isHeaderCheckable(int section)const
+{
+  return !this->model()->headerData(section, this->orientation(), Qt::CheckStateRole).isNull();
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCheckableModelHelper::isCheckable(const QModelIndex& index)const
+{
+  return !this->model()->data(index, Qt::CheckStateRole).isNull();
+}
+
+//-----------------------------------------------------------------------------
+Qt::CheckState ctkCheckableModelHelper::headerCheckState(int section)const
+{
+  return static_cast<Qt::CheckState>(
+    this->model()->headerData(section, this->orientation(), Qt::CheckStateRole).toInt());
+}
+
+//-----------------------------------------------------------------------------
+Qt::CheckState ctkCheckableModelHelper::checkState(const QModelIndex& index)const
+{
+  return static_cast<Qt::CheckState>(
+    this->model()->data(index, Qt::CheckStateRole).toInt());
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCheckableModelHelper::headerCheckState(int section, Qt::CheckState& checkState)const
+{
+  bool checkable = false;
+  checkState = static_cast<Qt::CheckState>(
+    this->model()->headerData(section, this->orientation(), Qt::CheckStateRole).toInt(&checkable));
+  return checkable;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCheckableModelHelper::checkState(const QModelIndex& index, Qt::CheckState& checkState)const
+{
+  bool checkable = false;
+  checkState = static_cast<Qt::CheckState>(
+    this->model()->data(index, Qt::CheckStateRole).toInt(&checkable));
+  return checkable;
+}
+    

+ 156 - 0
Libs/Core/ctkCheckableModelHelper.h

@@ -0,0 +1,156 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+/*=========================================================================
+
+   Program: ParaView
+   Module:    $RCSfile: pqCheckableModelHelper.h,v $
+
+   Copyright (c) 2005-2008 Sandia Corporation, Kitware Inc.
+   All rights reserved.
+
+   ParaView is a free software; you can redistribute it and/or modify it
+   under the terms of the ParaView license version 1.2. 
+
+   See License_v1.2.txt for the full ParaView license.
+   A copy of this license can be obtained by contacting
+   Kitware Inc.
+   28 Corporate Drive
+   Clifton Park, NY 12065
+   USA
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+=========================================================================*/
+
+#ifndef _ctkCheckableModelHelper_h
+#define _ctkCheckableModelHelper_h
+
+// Qt includes
+#include <QModelIndex>
+#include <QObject>
+class QAbstractItemModel;
+
+// CTK includes
+#include "ctkCoreExport.h"
+
+class ctkCheckableModelHelperPrivate;
+
+///
+/// ctkCheckableModelHelper expose functions to handle checkable models
+class CTK_CORE_EXPORT ctkCheckableModelHelper : public QObject
+{
+  Q_OBJECT;
+  Q_PROPERTY(bool forceCheckability READ forceCheckability WRITE setForceCheckability);
+  Q_PROPERTY(int propagateDepth READ propagateDepth WRITE setPropagateDepth);
+  Q_PROPERTY(Qt::CheckState defaultCheckState READ defaultCheckState WRITE setDefaultCheckState);
+  
+public:
+  ctkCheckableModelHelper(Qt::Orientation orientation, QObject *parent=0);
+  virtual ~ctkCheckableModelHelper();
+
+  Qt::Orientation orientation()const;
+  
+
+  ///
+  /// When setting the model, if PropagateToItems is true (by default), the check
+  /// state of the checkable headers is updated from the check state of the items
+  /// If you want to make sure of the check state of a header, after setting the
+  /// (done by myView.setHeader(myCheckableModelHelper)), you can call
+  /// myModel.setHeaderData(0, Qt::Horizontal, Qt::Checked, Qt::CheckStateRole)
+  /// or myCheckableModelHelper->setCheckState(0, Qt::Checked)
+  QAbstractItemModel *model()const;
+  virtual void setModel(QAbstractItemModel *model);
+
+  /// Reimplemented for internal reasons
+  QModelIndex rootIndex()const;
+  virtual void setRootIndex(const QModelIndex &index);
+
+  /// 
+  /// A section is checkable if its CheckStateRole data is non null. 
+  /// One can access the same value through the model:
+  /// model->headerData(orientation, section, Qt::CheckStateRole).isEmpty()
+  bool isHeaderCheckable(int section)const;
+  bool isCheckable(const QModelIndex& index)const;
+
+  ///
+  /// Utility function that returns the checkState of the section. 
+  /// One can access the same value through the model:
+  /// model->headerData(orientation, section, Qt::CheckStateRole)
+  Qt::CheckState headerCheckState(int section)const;
+  Qt::CheckState checkState(const QModelIndex&)const;
+
+  ///
+  /// Utility function that returns the checkState of the section. 
+  /// One can access the same value through the model:
+  /// model->headerData(orientation, section, Qt::CheckStateRole)
+  bool headerCheckState(int section, Qt::CheckState& checkState )const;
+  bool checkState(const QModelIndex&, Qt::CheckState& checkState )const;
+
+  /// How deep in the model(tree) do you want the check state to be propagated
+  /// A value of -1 correspond to the deepest level of the model.
+  /// -1 by default
+  void setPropagateDepth(int depth);
+  int  propagateDepth()const;
+  
+  /// When true, the new items are automatically set to checkable 
+  void setForceCheckability(bool force);
+  bool forceCheckability()const;
+
+  Qt::CheckState defaultCheckState()const;
+  void setDefaultCheckState(Qt::CheckState);
+
+public slots:
+  void setCheckState(const QModelIndex& modelIndex, Qt::CheckState checkState);
+  ///
+  /// Warning, setting the check state automatically set the 
+  /// header section checkable
+  void setHeaderCheckState(int section, Qt::CheckState checkState);
+
+  /// Utility function to toggle the checkstate of an index
+  void toggleCheckState(const QModelIndex& modelIndex);
+  void toggleHeaderCheckState(int section);
+  
+private slots:
+  void onHeaderDataChanged(Qt::Orientation orient, int first, int last);
+
+  void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
+  void updateHeadersFromItems();
+  void onColumnsInserted(const QModelIndex& parent, int start, int end);
+  void onRowsInserted(const QModelIndex& parent, int start, int end);
+
+protected:
+  QScopedPointer<ctkCheckableModelHelperPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkCheckableModelHelper);
+  Q_DISABLE_COPY(ctkCheckableModelHelper);
+};
+
+#endif

+ 3 - 2
Libs/DICOM/Widgets/Testing/Cpp/ctkDICOMModelTest2.cpp

@@ -33,6 +33,7 @@
 
 // CTK includes
 #include "ctkCheckableHeaderView.h"
+#include <ctkCheckableModelHelper.h>
 
 // STD includes
 #include <iostream>
@@ -78,8 +79,8 @@ int ctkDICOMModelTest2( int argc, char * argv [] )
     headerView->setClickable(previousHeaderView->isClickable());
     headerView->setMovable(previousHeaderView->isMovable());
     headerView->setHighlightSections(previousHeaderView->highlightSections());
-    headerView->setPropagateDepth(-1);
-    headerView->setForceCheckability(true);
+    headerView->checkableModelHelper()->setPropagateDepth(-1);
+    headerView->checkableModelHelper()->setForceCheckability(true);
     viewer.setHeader(headerView);
     model.setHeaderData(0, Qt::Horizontal, Qt::Checked, Qt::CheckStateRole);
     qDebug() << "new: " << headerView->isHidden();

+ 2 - 1
Libs/DICOM/Widgets/ctkDICOMQueryRetrieveWidget.cpp

@@ -29,6 +29,7 @@
 
 /// CTK includes
 #include <ctkCheckableHeaderView.h>
+#include <ctkCheckableModelHelper.h>
 #include <ctkLogger.h>
 
 // ctkDICOMCore includes
@@ -114,7 +115,7 @@ void ctkDICOMQueryRetrieveWidgetPrivate::init()
   headerView->setClickable(previousHeaderView->isClickable());
   headerView->setMovable(previousHeaderView->isMovable());
   headerView->setHighlightSections(previousHeaderView->highlightSections());
-  headerView->setPropagateDepth(-1);
+  headerView->checkableModelHelper()->setPropagateDepth(-1);
   this->results->setHeader(headerView);
   // headerView is hidden because it was created with a visisble parent widget 
   headerView->setHidden(false);

+ 2 - 1
Libs/DICOM/Widgets/ctkDICOMServerNodeWidget.cpp

@@ -28,6 +28,7 @@
 
 /// CTK includes
 #include <ctkCheckableHeaderView.h>
+#include <ctkCheckableModelHelper.h>
 
 // ctkDICOMWidgets includes
 #include "ctkDICOMServerNodeWidget.h"
@@ -65,7 +66,7 @@ ctkDICOMServerNodeWidget::ctkDICOMServerNodeWidget(QWidget* parentWidget)
   headerView->setClickable(previousHeaderView->isClickable());
   headerView->setMovable(previousHeaderView->isMovable());
   headerView->setHighlightSections(previousHeaderView->highlightSections());
-  headerView->setPropagateDepth(-1);
+  headerView->checkableModelHelper()->setPropagateDepth(-1);
   d->NodeTable->setHorizontalHeader(headerView);
 
   d->RemoveButton->setEnabled(false);

+ 2 - 0
Libs/Widgets/Testing/Cpp/CMakeLists.txt

@@ -8,6 +8,7 @@ SET(TEST_SOURCES
   ctkCheckBoxPixmapsTest1.cpp
   ctkCheckableHeaderViewTest1.cpp
   ctkCheckableHeaderViewTest2.cpp
+  ctkCheckableModelHelperTest2.cpp
   ctkCheckablePushButtonTest1.cpp
   ctkCollapsibleButtonTest1.cpp
   ctkCollapsibleButtonTest2.cpp
@@ -113,6 +114,7 @@ SIMPLE_TEST( ctkButtonGroupTest1 )
 SIMPLE_TEST( ctkCheckBoxPixmapsTest1 )
 SIMPLE_TEST( ctkCheckableHeaderViewTest1 )
 SIMPLE_TEST( ctkCheckableHeaderViewTest2 )
+SIMPLE_TEST( ctkCheckableModelHelperTest2 )
 SIMPLE_TEST( ctkCheckablePushButtonTest1 )
 SIMPLE_TEST( ctkCollapsibleButtonTest1 )
 SIMPLE_TEST( ctkCollapsibleButtonTest2 )

+ 9 - 8
Libs/Widgets/Testing/Cpp/ctkCheckableHeaderViewTest1.cpp

@@ -30,6 +30,7 @@
 
 // CTK includes
 #include "ctkCheckableHeaderView.h"
+#include <ctkCheckableModelHelper.h>
 
 // STD includes
 #include <cstdlib>
@@ -121,17 +122,17 @@ int ctkCheckableHeaderViewTest1(int argc, char * argv [] )
   QFocusEvent focus(QEvent::FocusIn,Qt::TabFocusReason);
   headerView->eventFilter(headerView, &focus);
   
-  if (headerView->propagateDepth() == 0)
+  if (headerView->checkableModelHelper()->propagateDepth() == 0)
     {
     std::cerr << "ctkCheckableHeaderView::propagateDepth() failed: "
-              << headerView->propagateDepth() << std::endl;
+              << headerView->checkableModelHelper()->propagateDepth() << std::endl;
     return EXIT_FAILURE;
     }
-  headerView->setPropagateDepth(0);
-  if (headerView->propagateDepth() != 0)
+  headerView->checkableModelHelper()->setPropagateDepth(0);
+  if (headerView->checkableModelHelper()->propagateDepth() != 0)
     {
     std::cerr << "ctkCheckableHeaderView::propagateDepth() failed: "
-              << headerView->propagateDepth() << std::endl;
+              << headerView->checkableModelHelper()->propagateDepth() << std::endl;
     return EXIT_FAILURE;
     }
   if (headerView->checkState(0) != Qt::Unchecked ||
@@ -148,7 +149,7 @@ int ctkCheckableHeaderViewTest1(int argc, char * argv [] )
     }
 
   // check the header
-  headerView->toggleCheckState(0);
+  headerView->checkableModelHelper()->toggleHeaderCheckState(0);
   
   // make sure it didn't uncheck the checkable items
   if (headerView->checkState(0) != Qt::Checked ||
@@ -180,9 +181,9 @@ int ctkCheckableHeaderViewTest1(int argc, char * argv [] )
     }
 
   // The checkable header gets updated with the item check states
-  headerView->setPropagateDepth(-1);
+  headerView->checkableModelHelper()->setPropagateDepth(-1);
 
-  if (headerView->propagateDepth() == 0 ||
+  if (headerView->checkableModelHelper()->propagateDepth() == 0 ||
       headerView->checkState(0) != Qt::PartiallyChecked ||
       row0[0]->checkState() != Qt::Checked ||
       row1[0]->checkState() != Qt::Unchecked ||

+ 3 - 2
Libs/Widgets/Testing/Cpp/ctkCheckableHeaderViewTest2.cpp

@@ -29,6 +29,7 @@
 
 // CTK includes
 #include "ctkCheckableHeaderView.h"
+#include <ctkCheckableModelHelper.h>
 
 // STD includes
 #include <cstdlib>
@@ -70,8 +71,8 @@ int ctkCheckableHeaderViewTest2(int argc, char * argv [] )
   headerView->setClickable(oldClickable);
   headerView->setMovable(previousHeaderView->isMovable());
   headerView->setHighlightSections(previousHeaderView->highlightSections());
-  headerView->setPropagateDepth(-1);
-  headerView->setForceCheckability(true);
+  headerView->checkableModelHelper()->setPropagateDepth(-1);
+  headerView->checkableModelHelper()->setForceCheckability(true);
 
   // sets the model to the headerview
   view.setHeader(headerView);

+ 89 - 0
Libs/Widgets/Testing/Cpp/ctkCheckableModelHelperTest2.cpp

@@ -0,0 +1,89 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QDebug>
+#include <QApplication>
+#include <QFocusEvent>
+#include <QTreeView>
+#include <QStandardItem>
+#include <QStandardItemModel>
+#include <QTimer>
+
+// CTK includes
+#include "ctkCheckableModelHelper.h"
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+//-----------------------------------------------------------------------------
+int ctkCheckableModelHelperTest2(int argc, char * argv [] )
+{
+  QApplication app(argc, argv);
+
+  QStringList headers;
+  headers << "Title 1" << "Title 2" << "Title 3";
+  QStandardItemModel model;
+  model.setHorizontalHeaderLabels(headers);
+  QList<QStandardItem*> row0;
+  row0 << new QStandardItem << new QStandardItem << new QStandardItem;
+  row0[0]->setText("forced checkability");
+  model.appendRow(row0);
+  QList<QStandardItem*> row1;
+  row1 << new QStandardItem << new QStandardItem << new QStandardItem;
+  row1[0]->setCheckable(true);
+  row1[0]->setText("checkable");
+  model.appendRow(row1);
+  QList<QStandardItem*> row2;
+  row2 << new QStandardItem << new QStandardItem << new QStandardItem;
+  row2[0]->setCheckable(true);
+  row2[0]->setText("checkable");
+  model.appendRow(row2);
+
+  QTreeView view;
+  view.setModel(&model);
+
+  model.setHeaderData(0, Qt::Horizontal, Qt::Checked, Qt::CheckStateRole);
+
+  ctkCheckableModelHelper headerView(Qt::Horizontal);
+  headerView.setPropagateDepth(-1);
+  headerView.setForceCheckability(true);
+  headerView.setDefaultCheckState(Qt::Checked);
+  headerView.setModel(&model);
+
+  
+  QList<QStandardItem*> subRow2;
+  subRow2 << new QStandardItem << new QStandardItem << new QStandardItem;
+//  subRow2[0]->setCheckable(true);
+  subRow2[0]->setText("checkable");
+  row2[0]->insertRow(0, subRow2);
+  
+  headers << "4";
+  model.setHorizontalHeaderLabels(headers);
+  view.show();
+
+  if (argc < 2 || QString(argv[1]) != "-I" )
+    {
+    QTimer::singleShot(500, &app, SLOT(quit()));
+    }
+  
+  return app.exec();
+}

+ 46 - 356
Libs/Widgets/ctkCheckableHeaderView.cpp

@@ -62,6 +62,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // CTK includes
 #include "ctkCheckableHeaderView.h"
+#include <ctkCheckableModelHelper.h>
 #include "ctkCheckBoxPixmaps.h"
 
 //-----------------------------------------------------------------------------
@@ -75,44 +76,21 @@ public:
   ~ctkCheckableHeaderViewPrivate();
 
   void init();
-  /// Set index checkstate and call propagate
-  void setIndexCheckState(const QModelIndex& index, Qt::CheckState checkState);
-  /// Return the depth in the model tree of the index.
-  /// -1 if the index is the root element a header or a header, 0 if the index
-  /// is a toplevel index, 1 if its parent is toplevel, 2 if its grandparent is
-  /// toplevel, etc.
-  int indexDepth(const QModelIndex& modelIndex)const;
-  /// Set the checkstate of the index based on its children and grand children
-  void updateCheckState(const QModelIndex& modelIndex);
-  /// Set the check state of the index to all its children and grand children
-  void propagateCheckStateToChildren(const QModelIndex& modelIndex);
-
-  Qt::CheckState checkState(const QModelIndex& index, bool *checkable)const;
-  void setCheckState(const QModelIndex& index, Qt::CheckState newCheckState);
 
+  ctkCheckableModelHelper* CheckableModelHelper;
   int                 Pressed;
   ctkCheckBoxPixmaps* CheckBoxPixmaps;
   bool                HeaderIsUpdating;
-  bool                ItemsAreUpdating;
-  bool                ForceCheckability;
-  /// 0 means no propagation
-  /// -1 means unlimited propagation
-  /// 1 means propagate to top-level indexes
-  /// 2 means propagate to top-level and their children
-  /// ...
-  int                 PropagateDepth;
 };
 
 //----------------------------------------------------------------------------
 ctkCheckableHeaderViewPrivate::ctkCheckableHeaderViewPrivate(ctkCheckableHeaderView& object)
   :q_ptr(&object)
 {
-  this->HeaderIsUpdating = false;
-  this->ItemsAreUpdating = false;
-  this->CheckBoxPixmaps = 0;
+  this->CheckableModelHelper = 0;
   this->Pressed = -1;
-  this->ForceCheckability = false;
-  this->PropagateDepth = -1;
+  this->CheckBoxPixmaps = 0;
+  this->HeaderIsUpdating = false;
 }
 
 //-----------------------------------------------------------------------------
@@ -123,6 +101,11 @@ ctkCheckableHeaderViewPrivate::~ctkCheckableHeaderViewPrivate()
     delete this->CheckBoxPixmaps;
     this->CheckBoxPixmaps = 0;
     }
+  if (this->CheckableModelHelper)
+    {
+    delete this->CheckableModelHelper;
+    this->CheckableModelHelper = 0;
+    }
 }
 
 //----------------------------------------------------------------------------
@@ -130,178 +113,56 @@ void ctkCheckableHeaderViewPrivate::init()
 {
   Q_Q(ctkCheckableHeaderView);
   this->CheckBoxPixmaps = new ctkCheckBoxPixmaps(q);
+  this->CheckableModelHelper = new ctkCheckableModelHelper(q->orientation(), q);
 }
 
 //----------------------------------------------------------------------------
-Qt::CheckState ctkCheckableHeaderViewPrivate::checkState(
-  const QModelIndex& index, bool *checkable)const
-{
-  Q_Q(const ctkCheckableHeaderView);
-  QVariant indexCheckState = index != q->rootIndex() ?
-    q->model()->data(index, Qt::CheckStateRole):
-    q->model()->headerData(0, q->orientation(), Qt::CheckStateRole);
-  return static_cast<Qt::CheckState>(indexCheckState.toInt(checkable));
-}
-
-//----------------------------------------------------------------------------
-void ctkCheckableHeaderViewPrivate::setCheckState(
-  const QModelIndex& modelIndex, Qt::CheckState newCheckState)
-{
-  Q_Q(ctkCheckableHeaderView);
-  if (modelIndex != q->rootIndex())
-    {
-    q->model()->setData(modelIndex, newCheckState, Qt::CheckStateRole);
-    }
-  else
-    {
-    q->model()->setHeaderData(0, q->orientation(), newCheckState, Qt::CheckStateRole);
-    }
-}
-
-//----------------------------------------------------------------------------
-void ctkCheckableHeaderViewPrivate::setIndexCheckState(
-  const QModelIndex& index, Qt::CheckState checkState)
+ctkCheckableHeaderView::ctkCheckableHeaderView(
+  Qt::Orientation orient, QWidget *widgetParent)
+  : QHeaderView(orient, widgetParent)
+  , d_ptr(new ctkCheckableHeaderViewPrivate(*this))
 {
-  bool checkable = false;
-  this->checkState(index, &checkable);
-  if (!checkable && !this->ForceCheckability)
+  Q_D(ctkCheckableHeaderView);
+  d->init();
+  // TODO: doesn't support reparenting here.
+  if(widgetParent)
     {
-    // The index is not checkable and we don't want to force checkability
-    return;
+    // Listen for focus change events.
+    widgetParent->installEventFilter(this);
     }
-  this->setCheckState(index, checkState);
-  this->propagateCheckStateToChildren(index);
 }
 
 //-----------------------------------------------------------------------------
-int ctkCheckableHeaderViewPrivate::indexDepth(const QModelIndex& modelIndex)const
+ctkCheckableHeaderView::~ctkCheckableHeaderView()
 {
-  int depth = -1;
-  QModelIndex parentIndex = modelIndex;
-  while (parentIndex.isValid())
-    {
-    ++depth;
-    parentIndex = parentIndex.parent();
-    }
-  return depth;
 }
 
 //-----------------------------------------------------------------------------
-void ctkCheckableHeaderViewPrivate
-::updateCheckState(const QModelIndex& modelIndex)
+Qt::CheckState ctkCheckableHeaderView::checkState(int section)const
 {
-  Q_Q(ctkCheckableHeaderView);
-  bool checkable = false;
-  int oldCheckState = this->checkState(modelIndex, &checkable);
-  if (!checkable)
-    {
-    return;
-    }
-
-  Qt::CheckState newCheckState = Qt::PartiallyChecked;
-  bool firstCheckableChild = true;
-  const int rowCount = q->orientation() == Qt::Horizontal ?
-    q->model()->rowCount(modelIndex) : 1;
-  const int columnCount = q->orientation() == Qt::Vertical ?
-    q->model()->columnCount(modelIndex) : 1;
-  for (int r = 0; r < rowCount; ++r)
-    {
-    for (int c = 0; c < columnCount; ++c)
-      {
-      QModelIndex child = q->model()->index(r, c, modelIndex);
-      QVariant childCheckState = q->model()->data(child, Qt::CheckStateRole);
-      int childState = childCheckState.toInt(&checkable);
-      if (!checkable)
-        {
-        continue;
-        }
-      if (firstCheckableChild)
-        {
-        newCheckState = static_cast<Qt::CheckState>(childState);
-        firstCheckableChild = false;
-        }
-      if (newCheckState != childState)
-        {
-        newCheckState = Qt::PartiallyChecked;
-        }
-      if (newCheckState == Qt::PartiallyChecked)
-        {
-        break;
-        }
-      }
-    if (!firstCheckableChild && newCheckState == Qt::PartiallyChecked)
-      {
-      break;
-      }
-    }
-  if (oldCheckState == newCheckState)
-    {
-    return;
-    }
-  this->setCheckState(modelIndex, newCheckState);
-  if (modelIndex != q->rootIndex())
-    {
-    this->updateCheckState(modelIndex.parent());
-    }
+  Q_D(const ctkCheckableHeaderView);
+  return d->CheckableModelHelper->headerCheckState(section);
 }
 
 //-----------------------------------------------------------------------------
-void ctkCheckableHeaderViewPrivate
-::propagateCheckStateToChildren(const QModelIndex& modelIndex)
+bool ctkCheckableHeaderView::checkState(int section, Qt::CheckState& checkState)const
 {
-  Q_Q(ctkCheckableHeaderView);
-  int indexDepth = this->indexDepth(modelIndex);
-  if (this->PropagateDepth == 0 ||
-      !(indexDepth < this->PropagateDepth || this->PropagateDepth == -1))
-    {
-    return;
-    }
-
-  bool checkable = false;
-  Qt::CheckState checkState = this->checkState(modelIndex, &checkable);
-  if (!checkable || checkState == Qt::PartiallyChecked)
-    {
-    return;
-    }
-
-  while (this->ForceCheckability && q->model()->canFetchMore(modelIndex))
-    {
-    q->model()->fetchMore(modelIndex);
-    }
-  
-  const int rowCount = q->orientation() == Qt::Horizontal ?
-    q->model()->rowCount(modelIndex) : 1;
-  const int columnCount = q->orientation() == Qt::Vertical ?
-    q->model()->columnCount(modelIndex) : 1;
-  for (int r = 0; r < rowCount; ++r)
-    {
-    for (int c = 0; c < columnCount; ++c)
-      {
-      QModelIndex child = q->model()->index(r, c, modelIndex);
-      this->setIndexCheckState(child, checkState);
-      }
-    }
+  Q_D(const ctkCheckableHeaderView);
+  return d->CheckableModelHelper->headerCheckState(section, checkState);
 }
 
-//----------------------------------------------------------------------------
-ctkCheckableHeaderView::ctkCheckableHeaderView(
-  Qt::Orientation orient, QWidget *widgetParent)
-  : QHeaderView(orient, widgetParent)
-  , d_ptr(new ctkCheckableHeaderViewPrivate(*this))
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::setCheckState(int section, Qt::CheckState checkState)
 {
   Q_D(ctkCheckableHeaderView);
-  d->init();
-  // TODO: doesn't support reparenting here.
-  if(widgetParent)
-    {
-    // Listen for focus change events.
-    widgetParent->installEventFilter(this);
-    }
+  d->CheckableModelHelper->setHeaderCheckState(section, checkState);
 }
 
 //-----------------------------------------------------------------------------
-ctkCheckableHeaderView::~ctkCheckableHeaderView()
+ctkCheckableModelHelper* ctkCheckableHeaderView::checkableModelHelper()const
 {
+  Q_D(const ctkCheckableHeaderView);
+  return d->CheckableModelHelper;
 }
 
 //-----------------------------------------------------------------------------
@@ -321,11 +182,7 @@ void ctkCheckableHeaderView::setModel(QAbstractItemModel *newModel)
 {
   Q_D(ctkCheckableHeaderView);
   QAbstractItemModel *current = this->model();
-  if (current == newModel)
-    {
-    return;
-    }
-  if(current)
+  if (current)
     {
     this->disconnect(
       current, SIGNAL(headerDataChanged(Qt::Orientation, int, int)),
@@ -334,17 +191,14 @@ void ctkCheckableHeaderView::setModel(QAbstractItemModel *newModel)
       current, SIGNAL(modelReset()),
       this, SLOT(updateHeaderPixmaps()));
     this->disconnect(
-      current, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
-      this, SLOT(onDataChanged(const QModelIndex&, const QModelIndex&)));
-    this->disconnect(
       current, SIGNAL(columnsInserted(const QModelIndex &, int, int)), 
-      this, SLOT(onHeaderSectionInserted(const QModelIndex &, int, int)));
+      this, SLOT(onHeaderSectionInserted()));
     this->disconnect(
       current, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
-      this, SLOT(onHeaderSectionInserted(const QModelIndex &, int, int)));
+      this, SLOT(onHeaderSectionInserted()));
     }
-
   this->QHeaderView::setModel(newModel);
+  d->CheckableModelHelper->setModel(newModel);
   if(newModel)
     {
     this->connect(
@@ -353,27 +207,19 @@ void ctkCheckableHeaderView::setModel(QAbstractItemModel *newModel)
     this->connect(
       newModel, SIGNAL(modelReset()),
       this, SLOT(updateHeaderPixmaps()));
-    if (d->PropagateDepth != 0)
-      {
-      this->connect(
-        newModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
-        this, SLOT(onDataChanged(const QModelIndex&, const QModelIndex&)));
-      this->updateHeadersFromItems();
-      }
     if(this->orientation() == Qt::Horizontal)
       {
       this->connect(
         newModel, SIGNAL(columnsInserted(const QModelIndex &, int, int)),
-        this, SLOT(onHeaderSectionInserted(const QModelIndex &, int, int)));
+        this, SLOT(onHeaderSectionInserted()));
       }
     else
       {
       this->connect(
         newModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
-        this, SLOT(onHeaderSectionInserted(const QModelIndex &, int, int)));
+        this, SLOT(onHeaderSectionInserted()));
       }
     }
-
   this->updateHeaderPixmaps();
 }
 
@@ -382,92 +228,7 @@ void ctkCheckableHeaderView::setRootIndex(const QModelIndex &index)
 {
   Q_D(ctkCheckableHeaderView);
   this->QHeaderView::setRootIndex(index);
-  if (d->PropagateDepth != 0)
-    {
-    this->updateHeadersFromItems();
-    }
-}
-
-//-----------------------------------------------------------------------------
-void ctkCheckableHeaderView::setPropagateDepth(int depth)
-{
-  Q_D(ctkCheckableHeaderView);
-  if (d->PropagateDepth == depth)
-    {
-    return;
-    }
-  d->PropagateDepth = depth;
-  if (!this->model())
-    {
-    return;
-    }
-  if (depth != 0)
-    {
-    this->connect(
-      this->model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
-      this, SLOT(onDataChanged(const QModelIndex&, const QModelIndex&)), Qt::UniqueConnection);
-    this->updateHeadersFromItems();
-    }
-  else
-    {
-    this->disconnect(
-      this->model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
-      this, SLOT(onDataChanged(const QModelIndex&, const QModelIndex&)));
-    }
-}
-
-//-----------------------------------------------------------------------------
-int ctkCheckableHeaderView::propagateDepth()const
-{
-  Q_D(const ctkCheckableHeaderView);
-  return d->PropagateDepth;
-}
-
-//-----------------------------------------------------------------------------
-void ctkCheckableHeaderView::setForceCheckability(bool force)
-{
-  Q_D(ctkCheckableHeaderView);
-  if (d->ForceCheckability == force)
-    {
-    return;
-    }
-  d->ForceCheckability = force;
-  if (this->model())
-    {
-    d->propagateCheckStateToChildren(this->rootIndex());
-    }
-}
-
-//-----------------------------------------------------------------------------
-bool ctkCheckableHeaderView::forceCheckability()const
-{
-  Q_D(const ctkCheckableHeaderView);
-  return d->ForceCheckability;
-}
-
-//-----------------------------------------------------------------------------
-void ctkCheckableHeaderView::toggleCheckState(int section)
-{
-  // If the section is checkable, toggle the check state.
-  if(!this->isCheckable(section))
-    {
-    return;
-    }
-  // I've no strong feeling to turn the state checked or unchecked when the 
-  // state is PartiallyChecked.
-  this->setCheckState(section, this->checkState(section) == Qt::Checked ? Qt::Unchecked : Qt::Checked);
-}
-
-//-----------------------------------------------------------------------------
-void ctkCheckableHeaderView::setCheckState(int section, Qt::CheckState checkState)
-{
-  QAbstractItemModel *current = this->model();
-  if(current == 0)
-    {
-    return;
-    }
-  current->setHeaderData(section, this->orientation(),
-                         checkState, Qt::CheckStateRole);
+  d->CheckableModelHelper->setRootIndex(index);
 }
 
 //-----------------------------------------------------------------------------
@@ -480,15 +241,8 @@ void ctkCheckableHeaderView::onHeaderDataChanged(Qt::Orientation orient,
     {
     return;
     }
-  bool oldItemsAreUpdating = d->ItemsAreUpdating;
-  if (!d->ItemsAreUpdating)
-    {
-    d->ItemsAreUpdating = true;
-    d->propagateCheckStateToChildren(this->rootIndex());
-    }
   // update pixmap
   this->updateHeaderPixmaps(firstSection, lastSection);
-  d->ItemsAreUpdating = oldItemsAreUpdating;
 }
 
 //-----------------------------------------------------------------------------
@@ -514,7 +268,7 @@ void ctkCheckableHeaderView::updateHeaderPixmaps(int firstSection, int lastSecti
     {
     QVariant decoration;
     Qt::CheckState checkState;
-    if (this->checkState(i, checkState))
+    if (d->CheckableModelHelper->headerCheckState(i, checkState))
       {
       decoration = d->CheckBoxPixmaps->pixmap(checkState, active);
       }
@@ -525,73 +279,9 @@ void ctkCheckableHeaderView::updateHeaderPixmaps(int firstSection, int lastSecti
 }
 
 //-----------------------------------------------------------------------------
-void ctkCheckableHeaderView::updateHeadersFromItems()
-{
-  Q_D(ctkCheckableHeaderView);
-  QAbstractItemModel *currentModel = this->model();
-  if (!currentModel)
-    {
-    return;
-    }
-  d->updateCheckState(QModelIndex());
-}
-
-//-----------------------------------------------------------------------------
-void ctkCheckableHeaderView::onDataChanged(const QModelIndex & topLeft,
-                                           const QModelIndex & bottomRight)
+void ctkCheckableHeaderView::onHeaderSectionInserted()
 {
-  Q_UNUSED(bottomRight);
-  Q_D(ctkCheckableHeaderView);
-  if(d->ItemsAreUpdating || d->PropagateDepth == 0)
-    {
-    return;
-    }
-  bool checkable = false;
-  d->checkState(topLeft, &checkable);
-  if (!checkable)
-    {
-    return;
-    }
-  d->ItemsAreUpdating = true;
-  // TODO: handle topLeft "TO bottomRight"
-  d->propagateCheckStateToChildren(topLeft);
-  d->updateCheckState(topLeft.parent());
-
-  d->ItemsAreUpdating = false;
-}
-
-//-----------------------------------------------------------------------------
-void ctkCheckableHeaderView::onHeaderSectionInserted(const QModelIndex &parentIndex,
-  int first, int last)
-{
-  // only handle toplevel columns.
-  if (this->rootIndex() != parentIndex)
-    {
-    return;
-    }
-  this->updateHeaderPixmaps(first, last);
-}
-
-//-----------------------------------------------------------------------------
-bool ctkCheckableHeaderView::isCheckable(int section)const
-{
-  return !this->model()->headerData(section, this->orientation(), Qt::CheckStateRole).isNull();
-}
-
-//-----------------------------------------------------------------------------
-Qt::CheckState ctkCheckableHeaderView::checkState(int section)const
-{
-  return static_cast<Qt::CheckState>(
-    this->model()->headerData(section, this->orientation(), Qt::CheckStateRole).toInt());
-}
-
-//-----------------------------------------------------------------------------
-bool ctkCheckableHeaderView::checkState(int section, Qt::CheckState& checkState)const
-{
-  bool checkable = false;
-  checkState = static_cast<Qt::CheckState>(
-    this->model()->headerData(section, this->orientation(), Qt::CheckStateRole).toInt(&checkable));
-  return checkable;
+  this->updateHeaderPixmaps();
 }
 
 //-----------------------------------------------------------------------------
@@ -609,7 +299,7 @@ void ctkCheckableHeaderView::mousePressEvent(QMouseEvent *e)
   //check if the check box is pressed
   int pos = this->orientation() == Qt::Horizontal ? e->x() : e->y();
   int section = this->logicalIndexAt(pos);
-  if (this->isCheckable(section) &&
+  if (d->CheckableModelHelper->isHeaderCheckable(section) &&
       this->isPointInCheckBox(section, e->pos()))
     {
     d->Pressed = section;
@@ -638,7 +328,7 @@ void ctkCheckableHeaderView::mouseReleaseEvent(QMouseEvent *e)
       this->isPointInCheckBox(section, e->pos()))
     {
     d->Pressed = -1;
-    this->toggleCheckState(section);
+    d->CheckableModelHelper->toggleHeaderCheckState(section);
     }
   this->QHeaderView::mousePressEvent(e);
 }

+ 12 - 34
Libs/Widgets/ctkCheckableHeaderView.h

@@ -56,11 +56,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <QHeaderView>
 
 // CTK includes
-#include <ctkPimpl.h>
-
 #include "ctkWidgetsExport.h"
 
 class ctkCheckableHeaderViewPrivate;
+class ctkCheckableModelHelper;
 
 ///
 /// ctkCheckableHeaderView is a QHeaderView that can display a checkbox 
@@ -74,8 +73,6 @@ class ctkCheckableHeaderViewPrivate;
 class CTK_WIDGETS_EXPORT ctkCheckableHeaderView : public QHeaderView
 {
   Q_OBJECT;
-  Q_PROPERTY(bool forceCheckability READ forceCheckability WRITE setForceCheckability);
-  Q_PROPERTY(int propagateDepth READ propagateDepth WRITE setPropagateDepth);
 public:
   ctkCheckableHeaderView(Qt::Orientation orient, QWidget *parent=0);
   virtual ~ctkCheckableHeaderView();
@@ -92,12 +89,15 @@ public:
   /// Reimplemented for internal reasons
   virtual void setRootIndex(const QModelIndex &index);
 
-  /// 
-  /// A section is checkable if its CheckStateRole data is non null. 
-  /// One can access the same value through the model:
-  /// model->headerData(orientation, section, Qt::CheckStateRole).isEmpty()
-  bool isCheckable(int section)const;
-
+  ///
+  ///  Used to listen for focus in/out events.
+  /// \param object The object receiving the event.
+  /// \param e Event specific data.
+  /// \return
+  ///   True if the event should be filtered out.
+  virtual bool eventFilter(QObject *object, QEvent *e);
+  
+  
   ///
   /// Utility function that returns the checkState of the section. 
   /// One can access the same value through the model:
@@ -109,41 +109,19 @@ public:
   /// One can access the same value through the model:
   /// model->headerData(orientation, section, Qt::CheckStateRole)
   bool checkState(int section,Qt::CheckState& checkState )const;
-
-  ///
-  ///  Used to listen for focus in/out events.
-  /// \param object The object receiving the event.
-  /// \param e Event specific data.
-  /// \return
-  ///   True if the event should be filtered out.
-  virtual bool eventFilter(QObject *object, QEvent *e);
-
-  /// How deep in the model(tree) do you want the check state to be propagated
-  /// A value of -1 correspond to the deepest level of the model.
-  /// -1 by default
-  void setPropagateDepth(int depth);
-  int  propagateDepth()const;
   
-  void setForceCheckability(bool force);
-  bool forceCheckability()const;
+  ctkCheckableModelHelper* checkableModelHelper()const;
 
 public slots:
   ///
-  /// if the check state is PartiallyChecked, the section becomes Checked
-  void toggleCheckState(int section);
-  ///
   /// Warning, setting the check state automatically set the 
   /// header section checkable
   void setCheckState(int section, Qt::CheckState checkState);
 
 private slots:
   void onHeaderDataChanged(Qt::Orientation orient, int first, int last);
-
-  void onHeaderSectionInserted(const QModelIndex &parent, int first, int last);
+  void onHeaderSectionInserted();
   inline void updateHeaderPixmaps();
-
-  void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
-  void updateHeadersFromItems();
   
 protected:
   virtual void updateHeaderPixmaps(int first, int last);