浏览代码

ENH: Added class ctkCollapsibleButton, ctkCollapsibleGroupBox, ctkCheckBoxPixmaps, ctkCheckableHeaderView

also added corresponding tests and qt designer plugins
Jean-Christophe Fillion-Robin 16 年之前
父节点
当前提交
d792cca88f

+ 15 - 0
Libs/Widgets/CMakeLists.txt

@@ -14,6 +14,16 @@ SET(KIT_include_directories
 SET(KIT_SRCS
   ctkAddRemoveComboBox.cpp
   ctkAddRemoveComboBox.h
+  ctkButtonGroup.cpp
+  ctkButtonGroup.h
+  ctkCheckableHeaderView.cpp
+  ctkCheckableHeaderView.h
+  ctkCheckBoxPixmaps.cpp
+  ctkCheckBoxPixmaps.h
+  ctkCollapsibleButton.cpp
+  ctkCollapsibleButton.h
+  ctkCollapsibleGroupBox.cpp
+  ctkCollapsibleGroupBox.h
   ctkColorPickerButton.cpp
   ctkColorPickerButton.h
   ctkMatrixWidget.cpp
@@ -31,6 +41,11 @@ SET(KIT_SRCS
 # Headers that should run through moc
 SET(KIT_MOC_SRCS
   ctkAddRemoveComboBox.h
+  ctkButtonGroup.h
+  ctkCheckableHeaderView.h
+  ctkCheckBoxPixmaps.h
+  ctkCollapsibleButton.h
+  ctkCollapsibleGroupBox.h
   ctkColorPickerButton.h
   ctkMatrixWidget.h
   ctkSettings.h

+ 6 - 6
Libs/Widgets/Plugins/CMakeLists.txt

@@ -13,10 +13,10 @@ SET(PLUGIN_SRCS
   ctkWidgetsAbstractPlugin.cpp
   ctkWidgetsAbstractPlugin.h
 
-#   ctkCollapsibleButtonPlugin.cpp
-#   ctkCollapsibleButtonPlugin.h
-#   ctkCollapsibleGroupBoxPlugin.cpp
-#   ctkCollapsibleGroupBoxPlugin.h
+  ctkCollapsibleButtonPlugin.cpp
+  ctkCollapsibleButtonPlugin.h
+  ctkCollapsibleGroupBoxPlugin.cpp
+  ctkCollapsibleGroupBoxPlugin.h
   ctkColorPickerButtonPlugin.cpp
   ctkColorPickerButtonPlugin.h
 #   ctkCoordinatesWidgetPlugin.cpp
@@ -49,8 +49,8 @@ SET(PLUGIN_SRCS
 SET(PLUGIN_MOC_SRCS
   ctkWidgetsPlugins.h
 
-#   ctkCollapsibleButtonPlugin.h
-#   ctkCollapsibleGroupBoxPlugin.h
+  ctkCollapsibleButtonPlugin.h
+  ctkCollapsibleGroupBoxPlugin.h
   ctkColorPickerButtonPlugin.h
 #   ctkCoordinatesWidgetPlugin.h
 #   ctkDoubleRangeSliderPlugin.h

+ 76 - 0
Libs/Widgets/Plugins/ctkCollapsibleButtonPlugin.cpp

@@ -0,0 +1,76 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+// CTK includes
+#include "ctkCollapsibleButtonPlugin.h"
+#include "ctkCollapsibleButton.h"
+
+//-----------------------------------------------------------------------------
+ctkCollapsibleButtonPlugin::ctkCollapsibleButtonPlugin(QObject *_parent)
+        : QObject(_parent)
+{
+}
+
+//-----------------------------------------------------------------------------
+QWidget *ctkCollapsibleButtonPlugin::createWidget(QWidget *_parent)
+{
+  ctkCollapsibleButton* _widget = new ctkCollapsibleButton(_parent);
+  return _widget;
+}
+
+//-----------------------------------------------------------------------------
+QString ctkCollapsibleButtonPlugin::domXml() const
+{
+  return "<widget class=\"ctkCollapsibleButton\" \
+          name=\"CTKCollapsibleButton\">\n"
+          " <property name=\"geometry\">\n"
+          "  <rect>\n"
+          "   <x>0</x>\n"
+          "   <y>0</y>\n"
+          "   <width>300</width>\n"
+          "   <height>100</height>\n"
+          "  </rect>\n"
+          " </property>\n"
+          " <property name=\"text\">"
+          "  <string>CollapsibleButton</string>"
+          " </property>"
+          " <property name=\"contentsFrameShape\">"
+          "  <enum>QFrame::StyledPanel</enum>"
+          " </property>"
+          "</widget>\n";
+}
+
+//-----------------------------------------------------------------------------
+QIcon ctkCollapsibleButtonPlugin::icon() const
+{
+  return QIcon(":/Icons/groupboxcollapsible.png");
+}
+
+//-----------------------------------------------------------------------------
+QString ctkCollapsibleButtonPlugin::includeFile() const
+{
+  return "ctkCollapsibleButton.h";
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCollapsibleButtonPlugin::isContainer() const
+{
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+QString ctkCollapsibleButtonPlugin::name() const
+{
+  return "ctkCollapsibleButton";
+}

+ 39 - 0
Libs/Widgets/Plugins/ctkCollapsibleButtonPlugin.h

@@ -0,0 +1,39 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+#ifndef __ctkCollapsibleButtonPlugin_h
+#define __ctkCollapsibleButtonPlugin_h
+
+// CTK includes
+#include "ctkWidgetsAbstractPlugin.h"
+
+class CTK_WIDGETS_PLUGINS_EXPORT ctkCollapsibleButtonPlugin :
+  public QObject,
+  public ctkWidgetsAbstractPlugin
+{
+  Q_OBJECT
+
+public:
+  ctkCollapsibleButtonPlugin(QObject *_parent = 0);
+  
+  QWidget *createWidget(QWidget *_parent);
+  QString  domXml() const;
+  QIcon    icon()const;
+  QString  includeFile() const;
+  bool     isContainer() const;
+  QString  name() const;
+  
+};
+
+#endif

+ 75 - 0
Libs/Widgets/Plugins/ctkCollapsibleGroupBoxPlugin.cpp

@@ -0,0 +1,75 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+// Qt includes
+#include <QDebug>
+
+// CTK includes
+#include "ctkCollapsibleGroupBoxPlugin.h"
+#include "ctkCollapsibleGroupBox.h"
+
+// --------------------------------------------------------------------------
+ctkCollapsibleGroupBoxPlugin::ctkCollapsibleGroupBoxPlugin(QObject *_parent) : QObject(_parent)
+{
+}
+
+// --------------------------------------------------------------------------
+QWidget *ctkCollapsibleGroupBoxPlugin::createWidget(QWidget *_parent)
+{
+  ctkCollapsibleGroupBox* _widget = new ctkCollapsibleGroupBox(_parent);
+  return _widget;
+}
+
+// --------------------------------------------------------------------------
+QString ctkCollapsibleGroupBoxPlugin::domXml() const
+{
+  return "<widget class=\"ctkCollapsibleGroupBox\" \
+          name=\"CTKCollapsibleGroupBox\">\n"
+          " <property name=\"geometry\">\n"
+          "  <rect>\n"
+          "   <x>0</x>\n"
+          "   <y>0</y>\n"
+          "   <width>300</width>\n"
+          "   <height>100</height>\n"
+          "  </rect>\n"
+          " </property>\n"
+          " <property name=\"title\">"
+          "  <string>GroupBox</string>"
+          " </property>"
+          "</widget>\n";
+}
+
+// --------------------------------------------------------------------------
+QIcon ctkCollapsibleGroupBoxPlugin::icon() const
+{
+  return QIcon(":/Icons/groupboxcollapsible.png");
+}
+
+// --------------------------------------------------------------------------
+QString ctkCollapsibleGroupBoxPlugin::includeFile() const
+{
+  return "ctkCollapsibleGroupBox.h";
+}
+
+// --------------------------------------------------------------------------
+bool ctkCollapsibleGroupBoxPlugin::isContainer() const
+{
+  return true;
+}
+
+// --------------------------------------------------------------------------
+QString ctkCollapsibleGroupBoxPlugin::name() const
+{
+  return "ctkCollapsibleGroupBox";
+}

+ 39 - 0
Libs/Widgets/Plugins/ctkCollapsibleGroupBoxPlugin.h

@@ -0,0 +1,39 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+#ifndef __ctkCollapsibleGroupBoxPlugin_h
+#define __ctkCollapsibleGroupBoxPlugin_h
+
+// CTK includes
+#include "ctkWidgetsAbstractPlugin.h"
+
+class CTK_WIDGETS_PLUGINS_EXPORT ctkCollapsibleGroupBoxPlugin :
+  public QObject,
+  public ctkWidgetsAbstractPlugin
+{
+  Q_OBJECT
+
+public:
+  ctkCollapsibleGroupBoxPlugin(QObject *_parent = 0);
+  
+  QWidget *createWidget(QWidget *_parent);
+  QString  domXml() const;
+  QIcon    icon() const;
+  QString  includeFile() const;
+  bool     isContainer() const;
+  QString  name() const;
+  
+};
+
+#endif

+ 4 - 4
Libs/Widgets/Plugins/ctkWidgetsPlugins.h

@@ -20,8 +20,8 @@
 
 // CTK includes
 #include "CTKWidgetsPluginsExport.h"
-// #include "ctkCollapsibleButtonPlugin.h"
-// #include "ctkCollapsibleGroupBoxPlugin.h"
+#include "ctkCollapsibleButtonPlugin.h"
+#include "ctkCollapsibleGroupBoxPlugin.h"
 #include "ctkColorPickerButtonPlugin.h"
 // #include "ctkCoordinatesWidgetPlugin.h"
 // #include "ctkDoubleRangeSliderPlugin.h"
@@ -46,8 +46,8 @@ public:
   QList<QDesignerCustomWidgetInterface*> customWidgets() const
     {
     QList<QDesignerCustomWidgetInterface *> plugins;
-    plugins //<< new ctkCollapsibleButtonPlugin
-//             << new ctkCollapsibleGroupBoxPlugin
+    plugins << new ctkCollapsibleButtonPlugin
+            << new ctkCollapsibleGroupBoxPlugin
             << new ctkColorPickerButtonPlugin;
 //             << new ctkCoordinatesWidgetPlugin
 //             << new ctkDoubleRangeSliderPlugin

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

@@ -2,6 +2,9 @@ SET(KIT ${PROJECT_NAME})
 
 CREATE_TEST_SOURCELIST(Tests ${KIT}CppTests.cxx
   ctkAddRemoveComboBoxTest1.cpp
+  ctkButtonGroupTest1.cpp
+  ctkCollapsibleButtonTest1.cpp
+  ctkCollapsibleGroupBoxTest1.cpp
   ctkColorPickerButtonTest1.cpp
   ctkMatrixWidgetTest1.cpp
   #EXTRA_INCLUDE TestingMacros.h
@@ -30,5 +33,8 @@ ENDMACRO( SIMPLE_TEST  )
 #
 
 SIMPLE_TEST( ctkAddRemoveComboBoxTest1 )
+SIMPLE_TEST( ctkButtonGroupTest1 )
+SIMPLE_TEST( ctkCollapsibleButtonTest1 )
+SIMPLE_TEST( ctkCollapsibleGroupBoxTest1 )
 SIMPLE_TEST( ctkColorPickerButtonTest1 )
 SIMPLE_TEST( ctkMatrixWidgetTest1 )

+ 127 - 0
Libs/Widgets/Testing/Cpp/ctkButtonGroupTest1.cpp

@@ -0,0 +1,127 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+// Qt includes
+#include <QApplication>
+#include <QPushButton>
+
+// CTK includes
+#include "ctkButtonGroup.h"
+#include "ctkCollapsibleButton.h"
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+int ctkButtonGroupTest1(int argc, char * argv [] )
+{
+  QApplication app(argc, argv);
+
+  QPushButton* button1 = new QPushButton(0);
+  QPushButton* button2 = new QPushButton(0);
+  QPushButton* button3 = new QPushButton(0);
+  ctkCollapsibleButton* button4 = new ctkCollapsibleButton(0);
+
+  button1->setCheckable(true);
+  button2->setCheckable(true);
+  button3->setCheckable(false);
+  button4->setCheckable(true);
+  
+  button2->setChecked(true);
+  button4->setChecked(true);
+  
+  ctkButtonGroup buttonGroup(0);
+  //QButtonGroup buttonGroup(0);
+
+  buttonGroup.addButton(button1);
+  buttonGroup.addButton(button2);
+  buttonGroup.addButton(button3);
+  buttonGroup.addButton(button4);
+
+  if (!button4->isChecked() || button2->isChecked())
+    {
+    std::cerr << "ctkButtonGroup::addButton failed"
+              << button2->isChecked() << " " << button4->isChecked() 
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  // Click #1: check button1
+  button1->click();
+  
+  if (!button1->isChecked() || button4->isChecked())
+    {
+    std::cerr << "ctkButtonGroup::click1 failed"
+              << button1->isChecked() << " " << button4->isChecked() 
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  // Click #2: uncheck button1 
+  button1->click();
+
+  if (button1->isChecked() || button4->isChecked())
+    {
+    std::cerr << "ctkButtonGroup::click2 failed"
+              << button1->isChecked() << " " << button4->isChecked() 
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  // Click #3: check button1 
+  button1->click();
+
+  if (!button1->isChecked() || button4->isChecked())
+    {
+    std::cerr << "ctkButtonGroup::click3 failed"
+              << button1->isChecked() << " " << button4->isChecked() 
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  // Click #4: check button2 
+  button2->click();
+
+  if (!button2->isChecked() || button1->isChecked())
+    {
+    std::cerr << "ctkButtonGroup::click4 failed"
+              << button2->isChecked() << " " << button1->isChecked() 
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+  
+  // Click #5: click button3 keep check on button2
+  button3->click();
+
+  if (!button2->isChecked() || button3->isChecked())
+    {
+    std::cerr << "ctkButtonGroup::click5 failed"
+              << button2->isChecked() << " " << button3->isChecked() 
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+  
+  // Click #6: uncheck button2
+  button2->click();
+
+  if (button2->isChecked())
+    {
+    std::cerr << "ctkButtonGroup::click6 failed"
+              << button2->isChecked() << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  return EXIT_SUCCESS;
+}
+

+ 34 - 0
Libs/Widgets/Testing/Cpp/ctkCollapsibleButtonTest1.cpp

@@ -0,0 +1,34 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+// Qt includes
+#include <QApplication>
+
+// CTK includes
+#include "ctkCollapsibleButton.h"
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+int ctkCollapsibleButtonTest1(int argc, char * argv [] )
+{
+  QApplication app(argc, argv);
+
+  ctkCollapsibleButton qctkObject;
+
+
+  return EXIT_SUCCESS;
+}
+

+ 34 - 0
Libs/Widgets/Testing/Cpp/ctkCollapsibleGroupBoxTest1.cpp

@@ -0,0 +1,34 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+// Qt includes
+#include <QApplication>
+
+// CTK includes
+#include "ctkCollapsibleGroupBox.h"
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+int ctkCollapsibleGroupBoxTest1(int argc, char * argv [] )
+{
+  QApplication app(argc, argv);
+
+  ctkCollapsibleGroupBox qctkObject;
+
+
+  return EXIT_SUCCESS;
+}
+

+ 61 - 0
Libs/Widgets/ctkButtonGroup.cpp

@@ -0,0 +1,61 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+//Qt includes  
+#include <QAbstractButton>
+#include <QDebug>
+#include <QWeakPointer>
+
+// CTK includes
+#include "ctkButtonGroup.h"
+
+//-----------------------------------------------------------------------------
+class ctkButtonGroupPrivate : public ctkPrivate<ctkButtonGroup>
+{
+public:
+  CTK_DECLARE_PUBLIC(ctkButtonGroup);
+  bool IsLastButtonPressedChecked;
+};
+
+//------------------------------------------------------------------------------
+ctkButtonGroup::ctkButtonGroup(QObject* _parent)
+  :QButtonGroup(_parent)
+{
+  CTK_INIT_PRIVATE(ctkButtonGroup);
+  connect(this, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(onButtonClicked(QAbstractButton*)));
+  connect(this, SIGNAL(buttonPressed(QAbstractButton*)), this, SLOT(onButtonPressed(QAbstractButton*)));
+}
+
+//------------------------------------------------------------------------------
+void ctkButtonGroup::onButtonClicked(QAbstractButton *clickedButton)
+{
+  CTK_D(ctkButtonGroup);
+  if (!this->exclusive() || !d->IsLastButtonPressedChecked)
+    {
+    return;
+    }
+  this->removeButton(clickedButton);
+  clickedButton->setChecked(false);
+  this->addButton(clickedButton);
+}
+
+//------------------------------------------------------------------------------
+void ctkButtonGroup::onButtonPressed(QAbstractButton *pressedButton)
+{
+  CTK_D(ctkButtonGroup);
+  Q_ASSERT(pressedButton);
+  d->IsLastButtonPressedChecked = pressedButton->isChecked();
+}
+
+

+ 47 - 0
Libs/Widgets/ctkButtonGroup.h

@@ -0,0 +1,47 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+#ifndef __ctkButtonGroup_h
+#define __ctkButtonGroup_h
+
+// Qt includes
+#include <QButtonGroup>
+
+// CTK includes
+#include <ctkPimpl.h>
+
+#include "CTKWidgetsExport.h"
+
+class ctkButtonGroupPrivate;
+
+///
+/// The ctkButtonGroup class is a QButtonGroup that has a slightly
+/// different behavior when exclusive. By default, a ctkButtonGroup is
+/// exclusive. An exclusive ctkButtonGroup switches off the button that
+///  was clicked if it was already checked.
+class CTK_WIDGETS_EXPORT ctkButtonGroup : public QButtonGroup
+{
+  Q_OBJECT
+public:
+  explicit ctkButtonGroup(QObject *_parent = 0);
+
+protected slots:
+  void onButtonClicked(QAbstractButton * button);
+  void onButtonPressed(QAbstractButton * button);
+
+private:
+  CTK_DECLARE_PRIVATE(ctkButtonGroup);
+};
+
+#endif

+ 108 - 0
Libs/Widgets/ctkCheckBoxPixmaps.cpp

@@ -0,0 +1,108 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+/*=========================================================================
+
+   Program: ParaView
+   Module:    $RCSfile: pqCheckBoxPixMaps.cxx,v $
+
+   Copyright (c) 2005,2006 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.
+
+========================================================================*/
+
+// Qt includes
+#include <QWidget>
+#include <QPainter>
+#include <QPixmap>
+#include <QStyle>
+#include <QStyleOptionButton>
+
+// CTK includes
+#include "ctkCheckBoxPixmaps.h"
+
+//-----------------------------------------------------------------------------
+ctkCheckBoxPixmaps::ctkCheckBoxPixmaps(QWidget* parentWidget)
+  : Superclass(parentWidget)
+{
+  Q_ASSERT(parentWidget != 0);
+
+  // Initialize the pixmaps. The following style array should
+  // correspond to the PixmapStateIndex enum.
+  const QStyle::State PixmapStyle[] =
+    {
+    QStyle::State_On | QStyle::State_Enabled,
+    QStyle::State_NoChange | QStyle::State_Enabled,
+    QStyle::State_Off | QStyle::State_Enabled,
+    QStyle::State_On | QStyle::State_Enabled | QStyle::State_Active,
+    QStyle::State_NoChange | QStyle::State_Enabled | QStyle::State_Active,
+    QStyle::State_Off | QStyle::State_Enabled | QStyle::State_Active
+    };
+
+  QStyleOptionButton option;
+  QRect r = parentWidget->style()->subElementRect(
+      QStyle::SE_CheckBoxIndicator, &option, parentWidget);
+  option.rect = QRect(QPoint(0,0), r.size());
+  for(int i = 0; i < ctkCheckBoxPixmaps::PixmapCount; i++)
+    {
+    this->Pixmaps[i] = QPixmap(r.size());
+    this->Pixmaps[i].fill(QColor(0, 0, 0, 0));
+    QPainter painter(&this->Pixmaps[i]);
+    option.state = PixmapStyle[i];
+    parentWidget->style()->drawPrimitive(
+        QStyle::PE_IndicatorCheckBox, &option, &painter, parentWidget);
+    }
+}
+
+//-----------------------------------------------------------------------------
+QPixmap ctkCheckBoxPixmaps::getPixmap(Qt::CheckState state, bool active) const
+{
+  int offset = active ? 3 : 0;
+  switch (state)
+    {
+  case Qt::Checked:
+    return this->Pixmaps[offset + Checked];
+
+  case Qt::Unchecked:
+    return this->Pixmaps[offset + UnChecked];
+
+  case Qt::PartiallyChecked:
+    return this->Pixmaps[offset + PartiallyChecked];
+    }
+
+  return QPixmap();
+}
+
+

+ 97 - 0
Libs/Widgets/ctkCheckBoxPixmaps.h

@@ -0,0 +1,97 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+/*=========================================================================
+
+   Program: ParaView
+   Module:    $RCSfile: pqCheckBoxPixMaps.h,v $
+
+   Copyright (c) 2005,2006 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 __ctkCheckBoxPixmaps_h
+#define __ctkCheckBoxPixmaps_h
+
+/// Qt includes
+#include <QObject>
+#include <QPixmap>
+
+/// CTK includes
+#include "CTKWidgetsExport.h"
+
+class QWidget;
+
+/// ctkCheckBoxPixmaps is a helper class that can used to create pixmaps for
+/// checkboxs in various states. This is useful for showing checkboxes in qt-views.
+class CTK_WIDGETS_EXPORT ctkCheckBoxPixmaps : public QObject
+{
+  Q_OBJECT
+  typedef QObject Superclass;
+
+public:
+  /// parent cannot be NULL.
+  ctkCheckBoxPixmaps(QWidget* parent);
+
+  /// Returns a pixmap for the given state .
+  QPixmap getPixmap(Qt::CheckState state, bool active) const;
+  QPixmap getPixmap(int state, bool active) const
+    {
+    return this->getPixmap(static_cast<Qt::CheckState>(state), active);
+    }
+
+
+private:
+  Q_DISABLE_COPY(ctkCheckBoxPixmaps)
+
+  enum PixmapStateIndex
+    {
+    Checked                 = 0,
+    PartiallyChecked        = 1,
+    UnChecked               = 2,
+    
+    // All active states in lower half
+    Checked_Active          = 3,
+    PartiallyChecked_Active = 4,
+    UnChecked_Active        = 5,
+   
+    PixmapCount             = 6
+    };
+  QPixmap Pixmaps[6];
+};
+
+#endif
+
+

+ 600 - 0
Libs/Widgets/ctkCheckableHeaderView.cpp

@@ -0,0 +1,600 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+/*=========================================================================
+
+   Program: ParaView
+   Module:    $RCSfile: pqCheckableHeaderView.cxx,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.
+
+=========================================================================*/
+
+// Qt includes
+#include <QAbstractItemModel>
+#include <QApplication>
+#include <QDebug>
+#include <QEvent>
+#include <QList>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QPixmap>
+#include <QStyle>
+
+// CTK includes
+#include "ctkCheckableHeaderView.h"
+#include "ctkCheckBoxPixmaps.h"
+
+//-----------------------------------------------------------------------------
+class ctkCheckableHeaderViewPrivate: public ctkPrivate<ctkCheckableHeaderView>
+{
+  CTK_DECLARE_PUBLIC(ctkCheckableHeaderView)
+public:
+  ctkCheckableHeaderViewPrivate();
+  ~ctkCheckableHeaderViewPrivate();
+  void init();
+
+  int Pressed;
+  ctkCheckBoxPixmaps* CheckBoxPixmaps;
+  bool HeaderIsUpdating;
+  bool ItemsAreUpdating;
+  bool PropagateToItems;
+};
+
+//----------------------------------------------------------------------------
+ctkCheckableHeaderViewPrivate::ctkCheckableHeaderViewPrivate()
+{
+  this->HeaderIsUpdating = false;
+  this->ItemsAreUpdating = false;
+  this->CheckBoxPixmaps = 0;
+  this->Pressed = -1;
+  this->PropagateToItems = false;
+}
+
+//-----------------------------------------------------------------------------
+ctkCheckableHeaderViewPrivate::~ctkCheckableHeaderViewPrivate()
+{
+  if (this->CheckBoxPixmaps)
+    {
+    delete this->CheckBoxPixmaps;
+    this->CheckBoxPixmaps = 0;
+    }
+}
+
+//----------------------------------------------------------------------------
+void ctkCheckableHeaderViewPrivate::init()
+{
+  CTK_P(ctkCheckableHeaderView);
+  this->CheckBoxPixmaps = new ctkCheckBoxPixmaps(p);
+}
+
+//----------------------------------------------------------------------------
+ctkCheckableHeaderView::ctkCheckableHeaderView(
+  Qt::Orientation orient, QWidget *widgetParent)
+  : QHeaderView(orient, widgetParent)
+{
+  CTK_INIT_PRIVATE(ctkCheckableHeaderView);
+  ctk_d()->init();
+  if(widgetParent)
+    {
+    // Listen for focus change events.
+    widgetParent->installEventFilter(this);
+    }
+}
+
+//-----------------------------------------------------------------------------
+ctkCheckableHeaderView::~ctkCheckableHeaderView()
+{
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCheckableHeaderView::eventFilter(QObject *, QEvent *e)
+{
+  if(e->type() != QEvent::FocusIn && 
+     e->type() != QEvent::FocusOut)
+    {
+    return false;
+    }
+  this->updateHeaders();
+  return false;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::setModel(QAbstractItemModel *newModel)
+{
+  CTK_D(ctkCheckableHeaderView);
+  QAbstractItemModel *current = this->model();
+  if (current == newModel)
+    {
+    return;
+    }
+  if(current)
+    {
+    this->disconnect(
+      current, SIGNAL(headerDataChanged(Qt::Orientation, int, int)),
+      this, SLOT(updateHeaderData(Qt::Orientation, int, int)));
+    this->disconnect(
+      current, SIGNAL(modelReset()),
+      this, SLOT(updateHeaders()));
+    this->disconnect(
+      current, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
+      this, SLOT(updateHeadersFromItems(const QModelIndex&, const QModelIndex&)));
+    this->disconnect(
+      current, SIGNAL(columnsInserted(const QModelIndex &, int, int)), 
+      this, SLOT(insertHeaderSection(const QModelIndex &, int, int)));
+    this->disconnect(
+      current, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+      this, SLOT(insertHeaderSection(const QModelIndex &, int, int)));
+    }
+
+  this->QHeaderView::setModel(newModel);
+  if(newModel)
+    {
+    this->connect(
+      newModel, SIGNAL(headerDataChanged(Qt::Orientation, int, int)),
+      this, SLOT(updateHeaderData(Qt::Orientation, int, int)));
+    this->connect(
+      newModel, SIGNAL(modelReset()),
+      this, SLOT(updateHeaders()));
+    if (d->PropagateToItems)
+      {
+      this->connect(
+        newModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
+        this, SLOT(updateHeadersFromItems(const QModelIndex&, const QModelIndex&)));
+      }
+    if(this->orientation() == Qt::Horizontal)
+      {
+      this->connect(
+        newModel, SIGNAL(columnsInserted(const QModelIndex &, int, int)),
+        this, SLOT(insertHeaderSection(const QModelIndex &, int, int)));
+      }
+    else
+      {
+      this->connect(
+        newModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+        this, SLOT(insertHeaderSection(const QModelIndex &, int, int)));
+      }
+    }
+
+  // Determine which sections are clickable and setup the icons.
+  this->updateHeaders();
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::setRootIndex(const QModelIndex &index)
+{
+  this->QHeaderView::setRootIndex(index);
+  this->updateHeaders();
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::setPropagateToItems(bool propagate)
+{
+  CTK_D(ctkCheckableHeaderView);
+  d->PropagateToItems = propagate;
+  if (!this->model())
+    {
+    return;
+    }
+  if (propagate)
+    {
+    this->connect(
+      this->model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
+      this, SLOT(updateHeadersFromItems(const QModelIndex&, const QModelIndex&)));
+    }
+  else
+    {
+    this->disconnect(
+      this->model(), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
+      this, SLOT(updateHeadersFromItems(const QModelIndex&, const QModelIndex&)));
+    }
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCheckableHeaderView::propagateToItems()const
+{
+  CTK_D(const ctkCheckableHeaderView);
+  return d->PropagateToItems;
+}
+
+//-----------------------------------------------------------------------------
+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)
+{
+  // If the section is checkable, toggle the check state.
+  QAbstractItemModel *current = this->model();
+  if(current == 0)
+    {    
+    return;
+    }
+  // If the state is unchecked or partially checked, the state
+  // should be changed to checked.
+  current->setHeaderData(section, this->orientation(),
+                         checkState, Qt::CheckStateRole);
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::updateHeaderData(Qt::Orientation orient,
+                                               int first, int last)
+{
+  if(orient != this->orientation())
+    {
+    return;
+    }
+  this->updateHeaders(first, last);
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::updateHeaders(int first, int last)
+{
+  CTK_D(ctkCheckableHeaderView);
+  if(d->HeaderIsUpdating)
+    {
+    return;
+    }
+  d->HeaderIsUpdating = true;
+  QAbstractItemModel *current = this->model();
+  Q_ASSERT(current);
+
+  first = qBound(0, first, this->count() -1);
+  last = qBound(0, last, this->count() -1);
+
+  bool active = true;
+  if(this->parentWidget())
+    {
+    active = this->parentWidget()->hasFocus();
+    }
+  int maxJ = this->orientation() == Qt::Horizontal ? 
+    current->rowCount() : current->columnCount();
+  
+  for(int i = first; i <= last; i++)
+    {
+    QVariant decoration;
+    Qt::CheckState checkState;
+    if (this->checkState(i, checkState))
+      {
+      decoration = d->CheckBoxPixmaps->getPixmap(checkState, active);
+      if (d->PropagateToItems && 
+          checkState != Qt::PartiallyChecked &&
+          !d->ItemsAreUpdating)
+        {
+        for (int j = 0 ; j < maxJ; ++j)
+          {
+          QModelIndex index = this->orientation() == Qt::Horizontal ? 
+            current->index(j, i,this->rootIndex()) :
+            current->index(i, j,this->rootIndex()) ;
+          current->setData(index, checkState, Qt::CheckStateRole);
+          }
+        }
+      }
+    current->setHeaderData(i, this->orientation(), decoration,
+                           Qt::DecorationRole);
+    }
+  d->HeaderIsUpdating = false;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::updateHeadersFromItems(const QModelIndex & topLeft,
+                                                     const QModelIndex & bottomRight)
+{
+  CTK_D(ctkCheckableHeaderView);
+  if(d->ItemsAreUpdating || !d->PropagateToItems || 
+     topLeft.parent() != this->rootIndex())
+    {
+    return;
+    }
+  d->ItemsAreUpdating = true;
+  
+  QAbstractItemModel *current = this->model();
+  Q_ASSERT(current);
+
+  int lastI;
+  int lastJ;
+  if (this->orientation() == Qt::Horizontal)
+    {
+    lastI = this->count();
+    lastJ = current->rowCount();
+    }
+  else
+    {
+    lastI = this->count();
+    lastJ = current->columnCount();
+    }
+  
+  for(int i = 0; i <= lastI; ++i)
+    {
+    Qt::CheckState sectionState;
+    if (!this->checkState(i, sectionState))
+      {
+      continue;
+      }
+    bool itemIsCheckable = false;
+    // get the first item state
+    Qt::CheckState itemsState;
+    int j = 0;
+    for ( ; j <= lastJ; ++j)
+      {
+      QModelIndex index = this->orientation() == Qt::Horizontal ? 
+        current->index(j, i, topLeft.parent()) : 
+        current->index(i, j, topLeft.parent());
+      itemsState = static_cast<Qt::CheckState>(
+        index.data(Qt::CheckStateRole).toInt(&itemIsCheckable));
+      if (itemIsCheckable)
+        {
+        break;
+        }
+      }
+    if (j > lastJ)
+      {// the first item check state couldn't be found
+      continue;
+      }
+    // check the other states to make sure it is the same state
+    for (; j <= lastJ; ++j)
+      {
+      QModelIndex index = this->orientation() == Qt::Horizontal ? 
+        current->index(j, i, topLeft.parent()) : 
+        current->index(i, j, topLeft.parent());
+      Qt::CheckState itemState = 
+        static_cast<Qt::CheckState>(index.data(Qt::CheckStateRole).toInt(&itemIsCheckable));
+      if (itemIsCheckable && itemState!= itemsState)
+        {// there is at least 1 item with a different state
+        this->setCheckState(i, Qt::PartiallyChecked);
+        break;
+        }
+      }
+    if (j > lastJ)
+      {
+      this->setCheckState(i, itemsState);
+      }
+    }
+  d->ItemsAreUpdating = false;
+}
+
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::insertHeaderSection(const QModelIndex &parentIndex,
+    int first, int last)
+{
+  if (this->rootIndex() != parentIndex)
+    {
+    return;
+    }
+  this->updateHeaders(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;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::mousePressEvent(QMouseEvent *e)
+{
+  CTK_D(ctkCheckableHeaderView);
+  if (e->button() != Qt::LeftButton || 
+      d->Pressed >= 0)
+    {
+    d->Pressed = -1;
+    this->QHeaderView::mousePressEvent(e);
+    return;
+    }
+  d->Pressed = -1;
+  //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) &&
+      this->isPointInCheckBox(section, e->pos()))
+    {
+    d->Pressed = section;
+    }
+  this->QHeaderView::mousePressEvent(e);
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::mouseReleaseEvent(QMouseEvent *e)
+{
+  CTK_D(ctkCheckableHeaderView);
+  if (e->button() != Qt::LeftButton || 
+      d->Pressed < 0)
+    {
+    d->Pressed = -1;
+    this->QHeaderView::mouseReleaseEvent(e);
+    return;
+    }
+  //check if the check box is pressed
+  int pos = this->orientation() == Qt::Horizontal ? e->x() : e->y();
+  int section = this->logicalIndexAt(pos);
+  if (section == d->Pressed && 
+      this->isPointInCheckBox(section, e->pos()))
+    {
+    d->Pressed = -1;
+    this->toggleCheckState(section);
+    }
+  this->QHeaderView::mousePressEvent(e);
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCheckableHeaderView::isPointInCheckBox(int section, QPoint pos)const
+{
+  QRect sectionRect = this->orientation() == Qt::Horizontal ? 
+    QRect(this->sectionPosition(section), 0, 
+          this->sectionSize(section), this->height()):
+    QRect(0, this->sectionPosition(section), 
+          this->width(), this->sectionSize(section));
+  QStyleOptionHeader opt;
+  this->initStyleOption(&opt);
+  this->initStyleSectionOption(&opt, section, sectionRect);
+  QRect headerLabelRect = this->style()->subElementRect(QStyle::SE_HeaderLabel, &opt, this);
+  // from qcommonstyle.cpp:1541
+  if (opt.icon.isNull()) 
+    {
+    return false;
+    }
+  QPixmap pixmap
+    = opt.icon.pixmap(this->style()->pixelMetric(QStyle::PM_SmallIconSize), 
+                      (opt.state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled);
+  QRect aligned = this->style()->alignedRect(opt.direction, QFlag(opt.iconAlignment), 
+                              pixmap.size(), headerLabelRect);
+  QRect inter = aligned.intersected(headerLabelRect);
+  return inter.contains(pos);
+}
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::initStyleSectionOption(QStyleOptionHeader *option, int section, QRect rect)const
+{
+  // from qheaderview.cpp:paintsection
+  QStyle::State state = QStyle::State_None;
+  if (this->isEnabled())
+    {
+    state |= QStyle::State_Enabled;
+    }
+  if (this->window()->isActiveWindow())
+    {
+    state |= QStyle::State_Active;
+    }
+  if (this->isSortIndicatorShown() && 
+      this->sortIndicatorSection() == section)
+    {
+    option->sortIndicator = (this->sortIndicatorOrder() == Qt::AscendingOrder)
+      ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
+    }
+
+  // setup the style option structure
+  QVariant textAlignment = 
+    this->model()->headerData(section, this->orientation(),
+                              Qt::TextAlignmentRole);
+  option->rect = rect;
+  option->section = section;
+  option->state |= state;
+  option->textAlignment = Qt::Alignment(textAlignment.isValid()
+                                        ? Qt::Alignment(textAlignment.toInt())
+                                        : this->defaultAlignment());
+  
+  option->iconAlignment = Qt::AlignVCenter;
+  option->text = this->model()->headerData(section, this->orientation(),
+                                  Qt::DisplayRole).toString();
+  if (this->textElideMode() != Qt::ElideNone)
+    {
+    option->text = option->fontMetrics.elidedText(option->text, this->textElideMode() , rect.width() - 4);
+    }
+
+  QVariant variant = this->model()->headerData(section, this->orientation(),
+                                          Qt::DecorationRole);
+  option->icon = qvariant_cast<QIcon>(variant);
+  if (option->icon.isNull())
+    {
+    option->icon = qvariant_cast<QPixmap>(variant);
+    }
+  QVariant foregroundBrush = this->model()->headerData(section, this->orientation(),
+                                                  Qt::ForegroundRole);
+  if (qVariantCanConvert<QBrush>(foregroundBrush))
+    {
+    option->palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
+    }
+
+  //QPointF oldBO = painter->brushOrigin();
+  QVariant backgroundBrush = this->model()->headerData(section, this->orientation(),
+                                                  Qt::BackgroundRole);
+  if (qVariantCanConvert<QBrush>(backgroundBrush)) 
+    {
+    option->palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
+    option->palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
+    //painter->setBrushOrigin(option->rect.topLeft());
+    }
+
+  // the section position
+  int visual = this->visualIndex(section);
+  Q_ASSERT(visual != -1);
+  if (this->count() == 1)
+    {
+    option->position = QStyleOptionHeader::OnlyOneSection;
+    }
+  else if (visual == 0)
+    {
+    option->position = QStyleOptionHeader::Beginning;
+    }
+  else if (visual == this->count() - 1)
+    {
+    option->position = QStyleOptionHeader::End;
+    }
+  else
+    {
+    option->position = QStyleOptionHeader::Middle;
+    }
+  option->orientation = this->orientation();
+  /* the selected position
+  bool previousSelected = d->isSectionSelected(this->logicalIndex(visual - 1));
+  bool nextSelected =  d->isSectionSelected(this->logicalIndex(visual + 1));
+  if (previousSelected && nextSelected)
+    option->selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected;
+  else if (previousSelected)
+    option->selectedPosition = QStyleOptionHeader::PreviousIsSelected;
+  else if (nextSelected)
+    option->selectedPosition = QStyleOptionHeader::NextIsSelected;
+  else
+    option->selectedPosition = QStyleOptionHeader::NotAdjacent;
+  */
+}
+    

+ 131 - 0
Libs/Widgets/ctkCheckableHeaderView.h

@@ -0,0 +1,131 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+/*=========================================================================
+
+   Program: ParaView
+   Module:    $RCSfile: pqCheckableHeaderView.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 _ctkCheckableHeaderView_h
+#define _ctkCheckableHeaderView_h
+
+// Qt includes
+#include <QHeaderView>
+
+// CTK includes
+#include <ctkPimpl.h>
+
+#include "CTKWidgetsExport.h"
+
+class ctkCheckableHeaderViewPrivate;
+
+class CTK_WIDGETS_EXPORT ctkCheckableHeaderView : public QHeaderView
+{
+  Q_OBJECT;
+  Q_PROPERTY(bool propagateToItems READ propagateToItems WRITE setPropagateToItems);
+public:
+  ctkCheckableHeaderView(Qt::Orientation orient, QWidget *parent=0);
+  virtual ~ctkCheckableHeaderView();
+
+  virtual void setModel(QAbstractItemModel *model);
+  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;
+
+  ///
+  /// 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 checkState(int section)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 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);
+                                                      
+  void setPropagateToItems(bool propagate);
+  bool propagateToItems()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 updateHeaderData(Qt::Orientation orient, int first, int last);
+  void insertHeaderSection(const QModelIndex &parent, int first, int last);
+  inline void updateHeaders();
+  void updateHeadersFromItems(const QModelIndex& topLeft, const QModelIndex& bottomRight);
+
+protected:
+  virtual void updateHeaders(int first, int last);
+  virtual void initStyleSectionOption(QStyleOptionHeader *option, int section, QRect rect)const;
+  virtual void mousePressEvent(QMouseEvent *e);
+  virtual void mouseReleaseEvent(QMouseEvent *e);
+  bool isPointInCheckBox(int section, QPoint pos)const;
+
+private:
+  CTK_DECLARE_PRIVATE(ctkCheckableHeaderView);
+};
+
+//-----------------------------------------------------------------------------
+void ctkCheckableHeaderView::updateHeaders()
+{
+  this->updateHeaders(0, this->count()-1);
+}
+
+#endif

+ 482 - 0
Libs/Widgets/ctkCollapsibleButton.cpp

@@ -0,0 +1,482 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+// Qt includes
+#include <QApplication>
+#include <QCleanlooksStyle>
+#include <QDebug>
+#include <QLayout>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QPushButton>
+#include <QStyle>
+#include <QStyleOptionButton>
+#include <QStyleOptionFrameV3>
+
+// CTK includes
+#include "ctkCollapsibleButton.h"
+
+//-----------------------------------------------------------------------------
+class ctkCollapsibleButtonPrivate : public ctkPrivate<ctkCollapsibleButton>
+{
+public:
+  CTK_DECLARE_PUBLIC(ctkCollapsibleButton);
+  void init();
+
+  bool     Collapsed;
+
+  // Contents frame
+  QFrame::Shape  ContentsFrameShape;
+  QFrame::Shadow ContentsFrameShadow;
+  int            ContentsLineWidth;
+  int            ContentsMidLineWidth;
+
+  int      CollapsedHeight;
+  bool     ExclusiveMouseOver;
+  bool     LookOffWhenChecked;
+
+  int      MaximumHeight;  // use carefully
+};
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButtonPrivate::init()
+{
+  CTK_P(ctkCollapsibleButton);
+  p->setCheckable(true);
+  // checked and Collapsed are synchronized: checked != Collapsed
+  p->setChecked(true);
+
+  this->Collapsed = false;
+
+  this->ContentsFrameShape = QFrame::NoFrame;
+  this->ContentsFrameShadow = QFrame::Plain;
+  this->ContentsLineWidth = 1;
+  this->ContentsMidLineWidth = 0;
+
+  this->CollapsedHeight = 10;
+  this->ExclusiveMouseOver = false;  
+  this->LookOffWhenChecked = true; // set as a prop ?
+  
+  this->MaximumHeight = p->maximumHeight();
+
+  p->setSizePolicy(QSizePolicy(QSizePolicy::Minimum,
+                               QSizePolicy::Preferred, 
+                               QSizePolicy::DefaultType));
+  p->setContentsMargins(0, p->buttonSizeHint().height(),0 , 0);
+
+  QObject::connect(p, SIGNAL(toggled(bool)),
+                   p, SLOT(onToggled(bool)));
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::initStyleOption(QStyleOptionButton* option)const
+{
+  CTK_D(const ctkCollapsibleButton);
+  if (option == 0)
+    {
+    return;
+    }
+  option->initFrom(this);
+
+  if (this->isDown() )
+    {
+    option->state |= QStyle::State_Sunken;
+    }
+  if (this->isChecked() && !d->LookOffWhenChecked)
+    {
+    option->state |= QStyle::State_On;
+    }
+  if (!this->isDown())
+    {
+    option->state |= QStyle::State_Raised;
+    }
+
+  option->text = this->text();
+  option->icon = this->icon();
+  option->iconSize = QSize(this->style()->pixelMetric(QStyle::PM_IndicatorWidth, option, this),
+                       this->style()->pixelMetric(QStyle::PM_IndicatorHeight, option, this));
+  int buttonHeight = this->buttonSizeHint().height();//qMax(this->fontMetrics().height(), option->iconSize.height());
+  option->rect.setHeight(buttonHeight);
+}
+
+//-----------------------------------------------------------------------------
+ctkCollapsibleButton::ctkCollapsibleButton(QWidget* _parent)
+  :QAbstractButton(_parent)
+{
+  CTK_INIT_PRIVATE(ctkCollapsibleButton);
+  ctk_d()->init();
+}
+
+//-----------------------------------------------------------------------------
+ctkCollapsibleButton::ctkCollapsibleButton(const QString& title, QWidget* _parent)
+  :QAbstractButton(_parent)
+{
+  CTK_INIT_PRIVATE(ctkCollapsibleButton);
+  ctk_d()->init();
+  this->setText(title);
+}
+
+//-----------------------------------------------------------------------------
+ctkCollapsibleButton::~ctkCollapsibleButton()
+{
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::setCollapsed(bool c)
+{
+  if (!this->isCheckable())
+    {
+    // not sure if one should handle this case...
+    this->collapse(c);
+    return;
+    }
+  this->setChecked(!c);
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCollapsibleButton::collapsed()const
+{
+  return ctk_d()->Collapsed;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::setCollapsedHeight(int h)
+{
+  ctk_d()->CollapsedHeight = h;
+  this->updateGeometry();
+}
+
+//-----------------------------------------------------------------------------
+int ctkCollapsibleButton::collapsedHeight()const
+{
+  return ctk_d()->CollapsedHeight;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::onToggled(bool checked)
+{
+  if (this->isCheckable())
+    {
+    this->collapse(!checked);
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::collapse(bool c)
+{
+  CTK_D(ctkCollapsibleButton);
+  if (c == d->Collapsed)
+    {
+    return;
+    }
+
+  d->Collapsed = c;
+
+  // we do that here as setVisible calls will correctly refresh the widget
+  if (c)
+    {
+    d->MaximumHeight = this->maximumHeight();
+    this->setMaximumHeight(this->sizeHint().height());
+    //this->updateGeometry();
+    }
+  else
+    {
+    // restore maximumheight
+    this->setMaximumHeight(d->MaximumHeight);
+    this->updateGeometry();
+    }
+
+  QObjectList childList = this->children();
+  for (int i = 0; i < childList.size(); ++i)
+    {
+    QObject *o = childList.at(i);
+    if (!o->isWidgetType())
+      {
+      continue;
+      }
+    QWidget *w = static_cast<QWidget *>(o);
+    w->setVisible(!c);
+    }
+  // this might be too many updates...
+  QWidget* _parent = this->parentWidget();
+  if (!d->Collapsed && (!_parent || !_parent->layout()))
+    {
+    this->resize(this->sizeHint());
+    }
+  else
+    {
+    this->updateGeometry();
+    }
+  //this->update(QRect(QPoint(0,0), this->sizeHint()));
+  //this->repaint(QRect(QPoint(0,0), this->sizeHint()));
+  emit contentsCollapsed(c);
+}
+
+//-----------------------------------------------------------------------------
+QFrame::Shape ctkCollapsibleButton::contentsFrameShape() const
+{
+  return ctk_d()->ContentsFrameShape;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::setContentsFrameShape(QFrame::Shape s)
+{
+  ctk_d()->ContentsFrameShape = s;
+}
+
+//-----------------------------------------------------------------------------
+QFrame::Shadow ctkCollapsibleButton::contentsFrameShadow() const
+{
+  return ctk_d()->ContentsFrameShadow;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::setContentsFrameShadow(QFrame::Shadow s)
+{
+  ctk_d()->ContentsFrameShadow = s;
+}
+
+//-----------------------------------------------------------------------------
+int ctkCollapsibleButton:: contentsLineWidth() const
+{
+  return ctk_d()->ContentsLineWidth;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::setContentsLineWidth(int w)
+{
+  ctk_d()->ContentsLineWidth = w;
+}
+
+//-----------------------------------------------------------------------------
+int ctkCollapsibleButton::contentsMidLineWidth() const
+{
+  return ctk_d()->ContentsMidLineWidth;
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::setContentsMidLineWidth(int w)
+{
+  ctk_d()->ContentsMidLineWidth = w;
+}
+
+//-----------------------------------------------------------------------------
+QSize ctkCollapsibleButton::buttonSizeHint()const
+{
+  int w = 0, h = 0;
+
+  QStyleOptionButton opt;
+  opt.initFrom(this);
+  
+  // indicator
+  QSize indicatorSize = QSize(style()->pixelMetric(QStyle::PM_IndicatorWidth, &opt, this),
+                              style()->pixelMetric(QStyle::PM_IndicatorHeight, &opt, this));
+  int indicatorSpacing = style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &opt, this);
+  int ih = indicatorSize.height();
+  int iw = indicatorSize.width() + indicatorSpacing;
+  w += iw;
+  h = qMax(h, ih);
+  
+  // text 
+  QString string(this->text());
+  bool empty = string.isEmpty();
+  if (empty)
+    {
+    string = QString::fromLatin1("XXXX");
+    }
+  QFontMetrics fm = this->fontMetrics();
+  QSize sz = fm.size(Qt::TextShowMnemonic, string);
+  if(!empty || !w)
+    {
+    w += sz.width();
+    }
+  h = qMax(h, sz.height());
+  //opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
+  QSize buttonSize = (style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(w, h), this).
+                      expandedTo(QApplication::globalStrut()));
+  return buttonSize;
+}
+
+//-----------------------------------------------------------------------------
+QSize ctkCollapsibleButton::minimumSizeHint()const
+{
+  CTK_D(const ctkCollapsibleButton);
+  QSize buttonSize = this->buttonSizeHint();
+  if (d->Collapsed)
+    {
+    return buttonSize + QSize(0,d->CollapsedHeight);
+    }
+  // open
+  if (this->layout() == 0)
+    {// no layout, means the button is empty ?
+    return buttonSize;
+    }
+  QSize s = this->QAbstractButton::minimumSizeHint(); 
+  return s.expandedTo(buttonSize);
+}
+
+//-----------------------------------------------------------------------------
+QSize ctkCollapsibleButton::sizeHint()const
+{
+  CTK_D(const ctkCollapsibleButton);
+  QSize buttonSize = this->buttonSizeHint();
+  if (d->Collapsed)
+    {
+    return buttonSize + QSize(0,d->CollapsedHeight);
+    }
+  // open
+  // QAbstractButton works well only if a layout is set
+  QSize s = this->QAbstractButton::sizeHint(); 
+  return s.expandedTo(buttonSize + QSize(0, d->CollapsedHeight));
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::paintEvent(QPaintEvent * _event)
+{
+  CTK_D(ctkCollapsibleButton);
+
+  QPainter p(this);
+  // Draw Button
+  QStyleOptionButton opt;
+  this->initStyleOption(&opt);
+
+  // We don't want to have the highlight effect on the button when mouse is
+  // over a child. We want the highlight effect only when the mouse is just
+  // over itself.
+  // same as this->underMouse()
+  bool exclusiveMouseOver = false;
+  if (opt.state & QStyle::State_MouseOver)
+    {
+    QRect buttonRect = opt.rect;
+    QList<QWidget*> _children = this->findChildren<QWidget*>();
+    QList<QWidget*>::ConstIterator it;
+    for (it = _children.constBegin(); it != _children.constEnd(); ++it ) 
+      {
+      if ((*it)->underMouse())
+        {
+        // the mouse has been moved from the collapsible button to one 
+        // of its children. The paint event rect is the child rect, this
+        // is why we have to request another paint event to redraw the 
+        // button to remove the highlight effect.
+        if (!_event->rect().contains(buttonRect))
+          {// repaint the button rect.
+          this->update(buttonRect);
+          }
+        opt.state &= ~QStyle::State_MouseOver;
+        exclusiveMouseOver = true;
+        break;
+        }
+      }
+    if (d->ExclusiveMouseOver && !exclusiveMouseOver)
+      {
+      // the mouse is over the widget, but not over the children. As it 
+      // has been de-highlighted in the past, we should refresh the button
+      // rect to re-highlight the button.
+      if (!_event->rect().contains(buttonRect))
+        {// repaint the button rect.
+        this->update(buttonRect);
+        }
+      }
+    }
+  d->ExclusiveMouseOver = exclusiveMouseOver;
+  QSize indicatorSize = QSize(style()->pixelMetric(QStyle::PM_IndicatorWidth, &opt, this),
+                              style()->pixelMetric(QStyle::PM_IndicatorHeight, &opt, this));
+  opt.iconSize = indicatorSize;
+  style()->drawControl(QStyle::CE_PushButtonBevel, &opt, &p, this);
+  // is PE_PanelButtonCommand better ?
+  //style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, &p, this);
+  int buttonHeight = opt.rect.height();
+
+  // Draw Indicator
+  QStyleOption indicatorOpt;
+  indicatorOpt.init(this);
+  indicatorOpt.rect = QRect((buttonHeight - indicatorSize.width()) / 2, 
+                            (buttonHeight - indicatorSize.height()) / 2,
+                            indicatorSize.width(), indicatorSize.height());
+  if (d->Collapsed)
+    {
+    style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &indicatorOpt, &p, this);
+    }
+  else
+    {
+    style()->drawPrimitive(QStyle::PE_IndicatorArrowUp, &indicatorOpt, &p, this);
+    }
+
+  // Draw Text
+  int indicatorSpacing = style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &opt, this);
+  opt.rect.setLeft( indicatorOpt.rect.right() + indicatorSpacing);
+  uint tf = Qt::AlignVCenter | Qt::AlignLeft;
+  if (this->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this))
+    {
+    tf |= Qt::TextShowMnemonic;
+    }
+  else
+    {
+    tf |= Qt::TextHideMnemonic;
+    }
+  style()->drawItemText(&p, opt.rect, tf, opt.palette, (opt.state & QStyle::State_Enabled),
+                        opt.text, QPalette::ButtonText);
+
+  // Draw Frame around contents
+  QStyleOptionFrameV3 f;
+  f.init(this);
+  // HACK: on some styles, the frame doesn't exactly touch the button.
+  // this is because the button has some kind of extra border. 
+  if (qobject_cast<QCleanlooksStyle*>(this->style()) != 0)
+    {
+    f.rect.setTop(buttonHeight - 1);
+    }
+  else
+    {
+    f.rect.setTop(buttonHeight);
+    }
+  f.frameShape = d->ContentsFrameShape;
+  switch (d->ContentsFrameShadow)
+    {
+    case QFrame::Sunken:
+      f.state |= QStyle::State_Sunken;
+      break;
+    case QFrame::Raised:
+      f.state |= QStyle::State_Raised;
+      break;
+    default:
+    case QFrame::Plain:
+      break;
+    }
+  f.lineWidth = d->ContentsLineWidth;
+  f.midLineWidth = d->ContentsMidLineWidth;
+  style()->drawControl(QStyle::CE_ShapedFrame, &f, &p, this);
+}
+
+//-----------------------------------------------------------------------------
+bool ctkCollapsibleButton::hitButton(const QPoint & _pos)const
+{
+  QStyleOptionButton opt;
+  this->initStyleOption(&opt);
+  return opt.rect.contains(_pos);
+}
+
+//-----------------------------------------------------------------------------
+void ctkCollapsibleButton::childEvent(QChildEvent* c)
+{
+  if(c && c->type() == QEvent::ChildAdded)
+    {
+    if (c->child() && c->child()->isWidgetType())
+      {
+      QWidget *w = static_cast<QWidget*>(c->child());
+      w->setVisible(!ctk_d()->Collapsed);
+      }
+    }
+  QWidget::childEvent(c);
+}

+ 120 - 0
Libs/Widgets/ctkCollapsibleButton.h

@@ -0,0 +1,120 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+#ifndef __ctkCollapsibleButton_h
+#define __ctkCollapsibleButton_h
+
+// Qt includes
+#include <QAbstractButton>
+#include <QFrame>
+
+// CTK includes
+#include <ctkPimpl.h>
+#include "CTKWidgetsExport.h"
+
+class ctkCollapsibleButtonPrivate;
+class QStyleOptionButton;
+
+/// Description
+/// A Collapsible widget that show/hide its children depending on its checked/collapsed properties
+class CTK_WIDGETS_EXPORT ctkCollapsibleButton : public QAbstractButton
+{
+  Q_OBJECT
+  Q_PROPERTY(bool collapsed READ collapsed WRITE setCollapsed DESIGNABLE isCheckable NOTIFY contentsCollapsed)
+  Q_PROPERTY(int collapsedHeight READ collapsedHeight WRITE setCollapsedHeight)
+
+  Q_PROPERTY(QFrame::Shape contentsFrameShape READ contentsFrameShape WRITE setContentsFrameShape)
+  Q_PROPERTY(QFrame::Shadow contentsFrameShadow READ contentsFrameShadow WRITE setContentsFrameShadow)
+  Q_PROPERTY(int contentsLineWidth READ contentsLineWidth WRITE setContentsLineWidth)
+  Q_PROPERTY(int contentsMidLineWidth READ contentsMidLineWidth WRITE setContentsMidLineWidth)
+
+public:
+  ctkCollapsibleButton(QWidget *parent = 0);
+  ctkCollapsibleButton(const QString& text, QWidget *parent = 0);
+  virtual ~ctkCollapsibleButton();
+
+  /// 
+  /// Property that describes if the widget is collapsed or not.
+  /// When collapsed, the children are invisible and the widget
+  /// has a sized defined by CollapsedHeight
+  void setCollapsed(bool);
+  bool collapsed()const;
+
+  /// 
+  /// Height used when the widget is collapsed
+  void setCollapsedHeight(int);
+  int collapsedHeight()const;
+
+  /// 
+  /// Set the frame shape of the contents
+  /// Hint: StyledPanel is probably the shape you are looking for
+  QFrame::Shape contentsFrameShape() const;
+  void setContentsFrameShape(QFrame::Shape);
+
+  /// 
+  /// Set the frame shadow of the contents
+  /// Hint: Raised is probably the shadow you are looking for
+  QFrame::Shadow contentsFrameShadow() const;
+  void setContentsFrameShadow(QFrame::Shadow);
+
+  /// 
+  /// Set the line width of the frame around the contents
+  int contentsLineWidth() const;
+  void setContentsLineWidth(int);
+
+  /// 
+  /// Set the mid line width of the frame around the contents
+  int contentsMidLineWidth() const;
+  void setContentsMidLineWidth(int);
+
+  /// 
+  /// Reimplement for internal reasons
+  virtual QSize minimumSizeHint()const;
+
+  /// 
+  /// Reimplement for internal reasons
+  virtual QSize sizeHint()const;
+
+signals:
+  /// 
+  /// Signal emitted when the widget is collapsed or expanded.
+  /// See signal toggled(bool) for the opposite.
+  void contentsCollapsed(bool);
+
+protected slots:
+  /// 
+  /// Perform the collapse.
+  virtual void collapse(bool c);
+  virtual void onToggled(bool clicked = false);
+
+protected:
+  virtual void paintEvent(QPaintEvent*);
+  //virtual void mousePressEvent(QMouseEvent* event);
+  //virtual void mouseReleaseEvent(QMouseEvent* event);
+  virtual void childEvent(QChildEvent* c);
+
+  virtual bool hitButton(const QPoint & pos) const;
+  /// Compute the size hint of the head button only. The sizehint depends on the text.
+  virtual QSize buttonSizeHint() const;
+
+  /// Initialize option with the values from this ctkCollapsibleButton.
+  /// This method is useful for subclasses when they need a QStyleOptionButton, 
+  /// but don't want to fill in all the information themselves.
+  virtual void initStyleOption(QStyleOptionButton* option)const;
+
+private:
+  CTK_DECLARE_PRIVATE(ctkCollapsibleButton);
+};
+
+#endif

+ 186 - 0
Libs/Widgets/ctkCollapsibleGroupBox.cpp

@@ -0,0 +1,186 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+// Qt includes
+#include <QDebug>
+#include <QChildEvent>
+#include <QMouseEvent>
+#include <QStylePainter>
+#include <QStyleOptionGroupBox>
+#include <QStyle>
+
+// CTK includes
+#include "ctkCollapsibleGroupBox.h"
+
+#if QT_VERSION >= 0x040600
+#include <QProxyStyle>
+
+class ctkCollapsibleGroupBoxStyle:public QProxyStyle
+{
+  public:
+
+  virtual void drawPrimitive(PrimitiveElement pe, const QStyleOption * opt, QPainter * p, const QWidget * widget = 0) const
+  {
+    if (pe == QStyle::PE_IndicatorCheckBox)
+      {
+      const QGroupBox* groupBox= qobject_cast<const QGroupBox*>(widget);
+      if (groupBox)
+        {
+        this->QProxyStyle::drawPrimitive(groupBox->isChecked() ? QStyle::PE_IndicatorArrowUp : QStyle::PE_IndicatorArrowDown, opt, p, widget);
+        return;
+        }
+      }
+    this->QProxyStyle::drawPrimitive(pe, opt, p, widget);
+  }
+};
+#else
+  
+#endif
+
+ctkCollapsibleGroupBox::ctkCollapsibleGroupBox(QWidget* _parent)
+  :QGroupBox(_parent)
+{
+  this->setCheckable(true);
+  connect(this, SIGNAL(toggled(bool)), this, SLOT(expand(bool)));
+
+  this->MaxHeight = this->maximumHeight();
+#if QT_VERSION >= 0x040600
+  this->setStyle(new ctkCollapsibleGroupBoxStyle);
+#else
+  this->setStyleSheet(
+    "ctkCollapsibleGroupBox::indicator:checked{"
+    "image: url(:/Icons/expand-up.png);}"
+    "ctkCollapsibleGroupBox::indicator:unchecked{"
+    "image: url(:/Icons/expand-down.png);}");
+#endif
+}
+
+ctkCollapsibleGroupBox::~ctkCollapsibleGroupBox()
+{
+
+}
+
+void ctkCollapsibleGroupBox::expand(bool _expand)
+{
+  if (!_expand)
+    {
+    this->OldSize = this->size();
+    }
+
+  QObjectList childList = this->children();
+  for (int i = 0; i < childList.size(); ++i) 
+    {
+    QObject *o = childList.at(i);
+    if (o && o->isWidgetType()) 
+      {
+      QWidget *w = static_cast<QWidget *>(o);
+      if ( w )
+        {
+        w->setVisible(_expand);
+        }
+      }
+    }
+  
+  if (_expand)
+    {
+    this->setMaximumHeight(this->MaxHeight);
+    this->resize(this->OldSize);
+    }
+  else
+    {
+    this->MaxHeight = this->maximumHeight();
+    this->setMaximumHeight(22);
+    }
+}
+
+void ctkCollapsibleGroupBox::childEvent(QChildEvent* c)
+{
+  if(c && c->type() == QEvent::ChildAdded)
+    {
+    if (c->child() && c->child()->isWidgetType())
+      {
+      QWidget *w = static_cast<QWidget*>(c->child());
+      w->setVisible(this->isChecked());
+      }
+    }
+  QGroupBox::childEvent(c);
+}
+
+#if QT_VERSION < 0x040600
+void ctkCollapsibleGroupBox::paintEvent(QPaintEvent* e)
+{
+  this->QGroupBox::paintEvent(e);
+  
+  QStylePainter paint(this);
+  QStyleOptionGroupBox option;
+  initStyleOption(&option);
+  option.activeSubControls &= !QStyle::SC_GroupBoxCheckBox;
+  paint.drawComplexControl(QStyle::CC_GroupBox, option);
+  
+}
+
+void ctkCollapsibleGroupBox::mousePressEvent(QMouseEvent *event)
+{
+    if (event->button() != Qt::LeftButton) {
+        event->ignore();
+        return;
+    }
+    // no animation
+}
+
+void ctkCollapsibleGroupBox::mouseReleaseEvent(QMouseEvent *event)
+{
+    if (event->button() != Qt::LeftButton) {
+        event->ignore();
+        return;
+    }
+
+    QStyleOptionGroupBox box;
+    initStyleOption(&box);
+    box.activeSubControls &= !QStyle::SC_GroupBoxCheckBox;
+    QStyle::SubControl released = style()->hitTestComplexControl(QStyle::CC_GroupBox, &box,
+                                                                 event->pos(), this);
+    bool toggle = this->isCheckable() && (released == QStyle::SC_GroupBoxLabel
+                                   || released == QStyle::SC_GroupBoxCheckBox);
+    if (toggle)
+      {
+      this->setChecked(!this->isChecked());
+      }
+}
+
+#endif
+
+QSize ctkCollapsibleGroupBox::minimumSizeHint() const
+{
+  //qDebug() << "ctkCollapsibleGroupBox::minimumSizeHint::" << this->QGroupBox::minimumSizeHint() ;
+  return this->QGroupBox::minimumSizeHint();
+}
+
+QSize ctkCollapsibleGroupBox::sizeHint() const
+{
+  //qDebug() << "ctkCollapsibleGroupBox::sizeHint::" << this->QGroupBox::sizeHint() ;
+  return this->QGroupBox::sizeHint();
+}
+
+int ctkCollapsibleGroupBox::heightForWidth(int w) const
+{
+  //qDebug() << "ctkCollapsibleGroupBox::heightForWidth::" << this->QGroupBox::heightForWidth(w) ;
+  return this->QGroupBox::heightForWidth(w);
+}
+
+void ctkCollapsibleGroupBox::resizeEvent ( QResizeEvent * _event )
+{
+  //qDebug() << "ctkCollapsibleGroupBox::resizeEvent::" << _event->oldSize() << _event->size() ;
+  return this->QGroupBox::resizeEvent(_event);
+}

+ 51 - 0
Libs/Widgets/ctkCollapsibleGroupBox.h

@@ -0,0 +1,51 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc. 
+  All rights reserved.
+  Distributed under a BSD License. See LICENSE.txt file.
+
+  This software is distributed "AS IS" WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  See the above copyright notice for more information.
+
+=========================================================================*/
+
+#ifndef __ctkCollapsibleGroupBox_h
+#define __ctkCollapsibleGroupBox_h
+
+// Qt includes
+#include <QGroupBox>
+
+// CTK includes
+#include "CTKWidgetsExport.h"
+
+class CTK_WIDGETS_EXPORT ctkCollapsibleGroupBox : public QGroupBox
+{
+  Q_OBJECT
+public:
+  explicit ctkCollapsibleGroupBox(QWidget* parent = 0);
+  virtual ~ctkCollapsibleGroupBox();
+  virtual int heightForWidth(int w) const;
+  virtual QSize minimumSizeHint()const;
+  virtual QSize sizeHint()const;
+
+protected slots:
+  virtual void expand(bool expand);
+
+protected:
+  virtual void childEvent(QChildEvent*);
+
+#if QT_VERSION < 0x040600
+  virtual void paintEvent(QPaintEvent*);
+  virtual void mousePressEvent(QMouseEvent*);
+  virtual void mouseReleaseEvent(QMouseEvent*);
+#endif
+  virtual void resizeEvent(QResizeEvent*);
+
+  QSize OldSize;
+  int   MaxHeight;
+};
+
+#endif