Просмотр исходного кода

Merge branch 'master' into plugin-framework

Conflicts:
	Libs/Core/CMakeLists.txt
Sascha Zelzer лет назад: 16
Родитель
Сommit
ffcbbeaa49
30 измененных файлов с 2524 добавлено и 172 удалено
  1. 0 1
      Applications/ctkDICOM/target_libraries.cmake
  2. 0 1
      Applications/ctkDICOMIndexer/target_libraries.cmake
  3. 1023 0
      CMake/ExternalProject.cmake
  4. 36 0
      CMake/ctkMacroAddCtkLibraryOptions.cmake
  5. 0 21
      CMake/ctkMacroGetTargetLibraries.cmake
  6. 45 0
      CMake/ctkMacroListFilter.cmake
  7. 0 6
      CMake/ctkMacroSetupQt.cmake
  8. 120 0
      CMake/ctkMacroTargetLibraries.cmake
  9. 38 5
      CMakeLists.txt
  10. 10 3
      Libs/Core/CMakeLists.txt
  11. 1 1
      Libs/Core/Testing/ctkUtilsTest1.cxx
  12. 516 0
      Libs/Core/ctkModelTester.cxx
  13. 79 0
      Libs/Core/ctkModelTester.h
  14. 2 2
      Libs/Core/target_libraries.cmake
  15. 24 4
      Libs/DICOM/Core/Testing/ctkDICOMModelTest1.cxx
  16. 179 36
      Libs/DICOM/Core/ctkDICOMModel.cxx
  17. 3 0
      Libs/DICOM/Core/ctkDICOMModel.h
  18. 1 1
      Libs/DICOM/Core/target_libraries.cmake
  19. 64 0
      Libs/Visualization/XIP/CMakeLists.txt
  20. 35 0
      Libs/Visualization/XIP/ctkXIPAdaptor.cxx
  21. 25 0
      Libs/Visualization/XIP/ctkXIPAdaptor.h
  22. 12 0
      Libs/Visualization/XIP/ctk_library_options.cmake
  23. 9 0
      Libs/Visualization/XIP/target_libraries.cmake
  24. 131 80
      SuperBuild.cmake
  25. 0 9
      Utilities/CMake/FindDCMTK.cmake
  26. 88 0
      Utilities/CMake/FindOpenIGTLink.cmake
  27. 24 0
      Utilities/CMake/FindPythonQt.cmake
  28. 40 0
      Utilities/QtMobility/QtMobilityBeta1-apple.patch
  29. 4 1
      Utilities/QtMobility/QtMobilityBeta1-patch.cmake.in
  30. 15 1
      Utilities/QtMobility/QtMobilityBeta1-win32.patch.in

+ 0 - 1
Applications/ctkDICOM/target_libraries.cmake

@@ -5,6 +5,5 @@
 # 
 
 SET(target_libraries
-  ${QT_LIBRARIES}
   CTKDICOMWidgets
   )

+ 0 - 1
Applications/ctkDICOMIndexer/target_libraries.cmake

@@ -5,6 +5,5 @@
 # 
 
 SET(target_libraries
-  ${QT_LIBRARIES}
   CTKDICOMCore
   )

Разница между файлами не показана из-за своего большого размера
+ 1023 - 0
CMake/ExternalProject.cmake


+ 36 - 0
CMake/ctkMacroAddCtkLibraryOptions.cmake

@@ -0,0 +1,36 @@
+
+#
+#
+#
+  
+MACRO(ctkMacroAddCtkLibraryOptions lib)
+
+  SET(filepath ${CMAKE_CURRENT_SOURCE_DIR}/Libs/${lib}/ctk_library_options.cmake)
+
+  # Add options only if "ctk_library_option.cmake" file exists
+  IF(EXISTS ${filepath})
+  
+    # Make sure the variable is cleared 
+    SET(ctk_library_options )
+
+    INCLUDE(${filepath})
+
+    FOREACH(option ${ctk_library_options})
+
+      # Make sure option is correctly formated
+      IF(NOT "${option}" MATCHES "^[A-Za-z0-9]+:(ON|OFF)")
+        MESSAGE(FATAL_ERROR "In ${filepath}, option ${option} is incorrect. Options should be specified using the following format OPT1:[ON|OFF]. For example OPT1:OFF or OPT2:ON")
+      ENDIF()
+      
+      # Extract option name and option default value
+      STRING(REPLACE ":" "\\;" option ${option})
+      SET(optionlist ${option})
+      LIST(GET optionlist 0 option_name)
+      LIST(GET optionlist 1 option_value)
+
+      OPTION(CTK_LIB_${lib}_${option_name} "Enable ${lib} Library ${option_name} option." ${option_value})
+    ENDFOREACH()
+    
+  ENDIF()
+  
+ENDMACRO()

+ 0 - 21
CMake/ctkMacroGetTargetLibraries.cmake

@@ -1,21 +0,0 @@
-
-#
-#
-#
-
-MACRO(ctkMacroGetTargetLibraries varname)
-
-  SET(target_libraries_path ${CMAKE_CURRENT_SOURCE_DIR}/target_libraries.cmake)
-  
-  # Check if "target_libraries.cmake" file exists
-  IF(NOT EXISTS ${target_libraries_path})
-    MESSAGE(FATAL_ERROR "${target_libraries_path} doesn't exists !")
-  ENDIF()
-
-  # Make sure the variable is cleared 
-  SET(target_libraries )
-
-  INCLUDE(${target_libraries_path})
-
-  SET(${varname} ${target_libraries})
-ENDMACRO()

+ 45 - 0
CMake/ctkMacroListFilter.cmake

@@ -0,0 +1,45 @@
+
+# See http://www.cmake.org/Wiki/CMakeMacroListOperations#LIST_FILTER
+
+# LIST_FILTER(<list> <regexp_var> [<regexp_var> ...]
+#              [OUTPUT_VARIABLE <variable>])
+# Removes items from <list> which do not match any of the specified
+# regular expressions. An optional argument OUTPUT_VARIABLE
+# specifies a variable in which to store the matched items instead of
+# updating <list>
+# As regular expressions can not be given to macros (see bug #5389), we pass
+# variable names whose content is the regular expressions.
+# Note that this macro requires PARSE_ARGUMENTS macro, available here:
+# http://www.cmake.org/Wiki/CMakeMacroParseArguments
+MACRO(CtkMacroListFilter)
+  ctkMacroParseArguments(LIST_FILTER "OUTPUT_VARIABLE" "" ${ARGV})
+  # Check arguments.
+  LIST(LENGTH LIST_FILTER_DEFAULT_ARGS LIST_FILTER_default_length)
+  IF(${LIST_FILTER_default_length} EQUAL 0)
+    MESSAGE(FATAL_ERROR "LIST_FILTER: missing list variable.")
+  ENDIF(${LIST_FILTER_default_length} EQUAL 0)
+  IF(${LIST_FILTER_default_length} EQUAL 1)
+    MESSAGE(FATAL_ERROR "LIST_FILTER: missing regular expression variable.")
+  ENDIF(${LIST_FILTER_default_length} EQUAL 1)
+  # Reset output variable
+  IF(NOT LIST_FILTER_OUTPUT_VARIABLE)
+    SET(LIST_FILTER_OUTPUT_VARIABLE "LIST_FILTER_internal_output")
+  ENDIF(NOT LIST_FILTER_OUTPUT_VARIABLE)
+  SET(${LIST_FILTER_OUTPUT_VARIABLE})
+  # Extract input list from arguments
+  LIST(GET LIST_FILTER_DEFAULT_ARGS 0 LIST_FILTER_input_list)
+  LIST(REMOVE_AT LIST_FILTER_DEFAULT_ARGS 0)
+  FOREACH(LIST_FILTER_item ${${LIST_FILTER_input_list}})
+    FOREACH(LIST_FILTER_regexp_var ${LIST_FILTER_DEFAULT_ARGS})
+      FOREACH(LIST_FILTER_regexp ${${LIST_FILTER_regexp_var}})
+        IF(${LIST_FILTER_item} MATCHES ${LIST_FILTER_regexp})
+          LIST(APPEND ${LIST_FILTER_OUTPUT_VARIABLE} ${LIST_FILTER_item})
+        ENDIF(${LIST_FILTER_item} MATCHES ${LIST_FILTER_regexp})
+      ENDFOREACH(LIST_FILTER_regexp ${${LIST_FILTER_regexp_var}})
+    ENDFOREACH(LIST_FILTER_regexp_var)
+  ENDFOREACH(LIST_FILTER_item)
+  # If OUTPUT_VARIABLE is not specified, overwrite the input list.
+  IF(${LIST_FILTER_OUTPUT_VARIABLE} STREQUAL "LIST_FILTER_internal_output")
+    SET(${LIST_FILTER_input_list} ${${LIST_FILTER_OUTPUT_VARIABLE}})
+  ENDIF(${LIST_FILTER_OUTPUT_VARIABLE} STREQUAL "LIST_FILTER_internal_output")
+ENDMACRO(CtkMacroListFilter)

+ 0 - 6
CMake/ctkMacroSetupQt.cmake

@@ -5,10 +5,6 @@
 
 MACRO(ctkMacroSetupQt)
 
-  IF(DEFINED CTK_QMAKE_EXECUTABLE)
-    SET(QT_QMAKE_EXECUTABLE ${CTK_QMAKE_EXECUTABLE})
-  ENDIF()
-
   SET(minimum_required_qt_version "4.6")
 
   FIND_PACKAGE(Qt4)
@@ -26,8 +22,6 @@ MACRO(ctkMacroSetupQt)
     SET(QT_USE_QTSQL ON)
     INCLUDE(${QT_USE_FILE})
 
-    SET(CTK_BASE_LIBRARIES ${CTK_BASE_LIBRARIES} ${QT_LIBRARIES} CACHE INTERNAL "CTK libraries" FORCE)
-    SET(CTK_QMAKE_EXECUTABLE ${QT_QMAKE_EXECUTABLE})
   ELSE(QT4_FOUND)
     MESSAGE(FATAL_ERROR "error: Qt4 was not found on your system. You probably need to set the QT_QMAKE_EXECUTABLE variable")
   ENDIF(QT4_FOUND)

+ 120 - 0
CMake/ctkMacroTargetLibraries.cmake

@@ -0,0 +1,120 @@
+
+#
+#
+#
+MACRO(ctkMacroGetTargetLibraries varname)
+
+  SET(filepath ${CMAKE_CURRENT_SOURCE_DIR}/target_libraries.cmake)
+
+  # Check if "target_libraries.cmake" file exists
+  IF(NOT EXISTS ${filepath})
+    MESSAGE(FATAL_ERROR "${filepath} doesn't exists !")
+  ENDIF()
+
+  # Make sure the variable is cleared
+  SET(target_libraries )
+
+  # Let's make sure target_libraries contains only strings
+  FILE(STRINGS "${filepath}" stringtocheck)
+  STRING(REGEX MATCHALL "[^\\#]\\$\\{.*\\}" incorrect_elements ${stringtocheck})
+  FOREACH(incorrect_element ${incorrect_elements})
+    STRING(REGEX REPLACE "\\$|\\{|\\}" "" correct_element ${incorrect_element})
+    MESSAGE(FATAL_ERROR "In ${filepath}, ${incorrect_element} should be replaced by ${correct_element}")
+  ENDFOREACH()
+
+  INCLUDE(${filepath})
+
+  # Loop over all target library, if it does *NOT* start with "CTK",
+  # let's resolve the variable to access its content
+  FOREACH(target_library ${target_libraries})
+    IF(${target_library} MATCHES "^CTK[a-zA-Z0-9]+$")
+      LIST(APPEND ${varname} ${target_library})
+    ELSE()
+      LIST(APPEND ${varname} "${${target_library}}")
+    ENDIF()
+  ENDFOREACH()
+ENDMACRO()
+
+
+#
+#
+#
+MACRO(ctkMacroGetAllTargetLibraries targets subdir varname)
+
+  FOREACH(target ${targets})
+    SET(option_prefix)
+    IF(${subdir} STREQUAL "Libs")
+      SET(option_prefix CTK_LIB_)
+    ELSEIF(${subdir} STREQUAL "Plugins")
+      SET(option_prefix CTK_PLUGIN_)
+    ELSEIF(${subdir} STREQUAL "Applications")
+      SET(option_prefix CTK_APP_)
+    ELSE()
+      MESSAGE(FATAL_ERROR "Unknown subdir:${subdir}, expected value are: 'Libs, 'Plugins' or 'Applications'")
+    ENDIF()
+
+    SET(option_name ${option_prefix}${target})
+    #MESSAGE(STATUS option_name:${option_name})
+    IF(${${option_name}})
+      #MESSAGE(STATUS target:${target})
+      SET(lib_targets)
+
+      SET(filepath ${CTK_SOURCE_DIR}/${subdir}/${target}/target_libraries.cmake)
+      #MESSAGE(STATUS filepath:${filepath})
+
+      # Check if "target_libraries.cmake" file exists
+      IF(NOT EXISTS ${filepath})
+        MESSAGE(FATAL_ERROR "${filepath} doesn't exists !")
+      ENDIF()
+
+      # Let's make sure target_libraries contains only strings
+      FILE(STRINGS "${filepath}" stringtocheck)
+      STRING(REGEX MATCHALL "[^#]\\$\\{.*\\}" incorrect_elements ${stringtocheck})
+      FOREACH(incorrect_element ${incorrect_elements})
+        STRING(REGEX REPLACE "\\$|\\{|\\}" "" correct_element ${incorrect_element})
+        MESSAGE(FATAL_ERROR "In ${filepath}, ${incorrect_element} should be replaced by ${correct_element}")
+      ENDFOREACH()
+
+      # Make sure the variable is cleared
+      SET(target_libraries )
+
+      INCLUDE(${filepath})
+
+      LIST(APPEND ${varname} ${target_libraries})
+      LIST(REMOVE_DUPLICATES ${varname})
+    ENDIF()
+  ENDFOREACH()
+  
+ENDMACRO()
+
+#
+# Extract all library names starting with CTK uppercase
+#
+MACRO(ctkMacroGetAllCTKTargetLibraries all_target_libraries varname)
+  SET(re_ctklib "^CTK[a-zA-Z0-9]+$")
+  LIST(APPEND _tmp_list ${all_target_libraries})
+  ctkMacroListFilter(_tmp_list re_ctklib OUTPUT_VARIABLE ${varname})
+  #MESSAGE(STATUS varname:${varname}:${${varname}})
+ENDMACRO()
+
+#
+# Extract all library names *NOT* starting with CTK uppercase
+#
+MACRO(ctkMacroGetAllNonCTKTargetLibraries all_target_libraries varname)
+  ctkMacroGetAllCTKTargetLibraries("${all_target_libraries}" all_ctk_libraries)
+  SET(_tmp_list ${all_target_libraries})
+  LIST(REMOVE_ITEM _tmp_list ${all_ctk_libraries})
+  SET(${varname} ${_tmp_list})
+  #MESSAGE(STATUS varname:${varname}:${${varname}})
+ENDMACRO()
+
+#
+# 
+#
+MACRO(ctkMacroShouldAddExternalProject libraries_variable_name resultvar)
+  LIST(FIND NON_CTK_DEPENDENCIES ${libraries_variable_name} index)
+  SET(${resultvar} FALSE)
+  IF(${index} GREATER -1)
+    SET(${resultvar} TRUE)
+  ENDIF()
+ENDMACRO()

+ 38 - 5
CMakeLists.txt

@@ -1,7 +1,10 @@
 CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
-IF(COMMAND CMAKE_POLICY)
-  CMAKE_POLICY(SET CMP0003 NEW)
-ENDIF(COMMAND CMAKE_POLICY)
+
+FOREACH(policy CMP0003)
+  IF(POLICY ${policy})
+    CMAKE_POLICY(SET ${policy} NEW)
+  ENDIF()
+ENDFOREACH()
 
 PROJECT(CTK)
 
@@ -48,11 +51,13 @@ SET(CTK_BASE_INCLUDE_DIRS CACHE INTERNAL "CTK includes" FORCE)
 # CMake Macro(s)
 # 
 INCLUDE(CMake/ctkMacroParseArguments.cmake)
+INCLUDE(CMake/ctkMacroListFilter.cmake)
 INCLUDE(CMake/ctkMacroBuildQtLib.cmake)
 INCLUDE(CMake/ctkMacroBuildQtPlugin.cmake)
 INCLUDE(CMake/ctkMacroBuildQtApp.cmake)
 INCLUDE(CMake/ctkMacroSetupQt.cmake)
-INCLUDE(CMake/ctkMacroGetTargetLibraries.cmake)
+INCLUDE(CMake/ctkMacroTargetLibraries.cmake) # Import multiple macros
+INCLUDE(CMake/ctkMacroAddCtkLibraryOptions.cmake)
 
 #-----------------------------------------------------------------------------
 # Patch program
@@ -103,6 +108,11 @@ ENDIF()
 #
 ctkMacroSetupQt()
 
+# Update CTK_BASE_LIBRARIES with QT libraries
+IF(QT4_FOUND)
+  SET(CTK_BASE_LIBRARIES ${CTK_BASE_LIBRARIES} ${QT_LIBRARIES} CACHE INTERNAL "CTK libraries" FORCE)
+ENDIF()
+
 #-----------------------------------------------------------------------------
 # CTK Libraries
 #
@@ -111,13 +121,14 @@ SET(ctk_libs
   Widgets
   DICOM/Core
   DICOM/Widgets
+  Visualization/XIP
   )
 
 #-----------------------------------------------------------------------------
 # CTK Plugins
 #
 SET(ctk_plugins
-  org.commontk.cli
+  #org.commontk.cli
   )
 
 #-----------------------------------------------------------------------------
@@ -143,8 +154,22 @@ OPTION(CTK_USE_KWSTYLE     "Enable sourcecode-based style tests." OFF)
 #MARK_AS_ADVANCED(CTK_USE_KWSTYLE)
 
 # Build options associated with CTK libraries
+# Note also that if
+# the file Libs/<DIR>/<LIBNAME>/ctk_library_options.cmake exists and look like:
+#
+#     SET(ctk_library_options
+#       OPT1:OFF
+#       OPT2:ON
+#       )
+#
+# In addition to 'CTK_LIB_Visualization/XIP' option, the following ones
+# will also be available in CMake configure menu:
+#  CTK_LIB_<DIR>/<LIBNAME>_OPT1  (set to OFF)
+#  CTK_LIB_<DIR>/<LIBNAME>_OPT2  (set to ON)
+#
 FOREACH(lib ${ctk_libs})
   OPTION(CTK_LIB_${lib} "Enable ${lib} Library." ON)
+  ctkMacroAddCtkLibraryOptions(${lib})
 ENDFOREACH()
 
 # Build options associated with CTK plugins
@@ -158,6 +183,14 @@ FOREACH(app ${ctk_applications})
 ENDFOREACH()
 
 #-----------------------------------------------------------------------------
+# Check dependencies
+#
+ctkMacroGetAllTargetLibraries("${ctk_libs}" "Libs" ALL_TARGET_LIBRARIES)
+ctkMacroGetAllTargetLibraries("${ctk_plugins}" "Plugins" ALL_TARGET_LIBRARIES)
+ctkMacroGetAllTargetLibraries("${ctk_applications}" "Applications" ALL_TARGET_LIBRARIES)
+#MESSAGE(STATUS ALL_TARGET_LIBRARIES:${ALL_TARGET_LIBRARIES})
+
+#-----------------------------------------------------------------------------
 # Superbuild is used by default
 #
 OPTION(CTK_SUPERBUILD "Build CTK and the projects it depends on via SuperBuild.cmake." ON)

+ 10 - 3
Libs/Core/CMakeLists.txt

@@ -1,5 +1,9 @@
 PROJECT(CTKCore)
 
+#
+# 3rd party dependencies
+#
+
 # use the QtMobility SuperBuild paths for now
 # we should add a FindQtMobility later
 SET(QTMOBILITY_INCLUDE_DIRS 
@@ -14,12 +18,12 @@ FIND_LIBRARY(QTMOBILITY_QTSERVICEFW_LIBRARY_RELEASE QtServiceFramework
              PATHS ${QTMOBILITY_LIBRARY_DIR}
              )
              
-SET(QTMOBILITY_QTSERVICEFW_LIBRARY )
+SET(QTMOBILITY_QTSERVICEFW_LIBRARIES )
 IF(QTMOBILITY_QTSERVICEFW_LIBRARY_RELEASE)
-  LIST(APPEND QTMOBILITY_QTSERVICEFW_LIBRARY optimized ${QTMOBILITY_QTSERVICEFW_LIBRARY_RELEASE})
+  LIST(APPEND QTMOBILITY_QTSERVICEFW_LIBRARIES optimized ${QTMOBILITY_QTSERVICEFW_LIBRARY_RELEASE})
 ENDIF()
 IF(QTMOBILITY_QTSERVICEFW_LIBRARY_DEBUG)
-  LIST(APPEND QTMOBILITY_QTSERVICEFW_LIBRARY debug ${QTMOBILITY_QTSERVICEFW_LIBRARY_DEBUG})
+  LIST(APPEND QTMOBILITY_QTSERVICEFW_LIBRARIES debug ${QTMOBILITY_QTSERVICEFW_LIBRARY_DEBUG})
 ENDIF()
              
 #
@@ -35,6 +39,8 @@ SET(KIT_include_directories
   
 # Source files
 SET(KIT_SRCS
+  ctkModelTester.cxx
+  ctkModelTester.h
   ctkUtils.cxx
   ctkUtils.h
 
@@ -45,6 +51,7 @@ SET(KIT_SRCS
 
 # Headers that should run through moc
 SET(KIT_MOC_SRCS
+  ctkModelTester.h
 
   # PluginFramework headers
   PluginFramework/ctkPluginActivator.h

+ 1 - 1
Libs/Core/Testing/ctkUtilsTest1.cxx

@@ -12,7 +12,7 @@
 
 =========================================================================*/
 
-// qCTK includes
+// CTK includes
 #include "ctkUtils.h"
 
 // QT includes

+ 516 - 0
Libs/Core/ctkModelTester.cxx

@@ -0,0 +1,516 @@
+/*=========================================================================
+
+  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.
+
+=========================================================================*/
+
+#include "ctkModelTester.h"
+
+#include <QDebug>
+#include <QStack>
+
+//-----------------------------------------------------------------------------
+class ctkModelTesterPrivate: public qCTKPrivate<ctkModelTester>
+{
+public:
+  ctkModelTesterPrivate();
+  QAbstractItemModel *Model;
+  bool ThrowOnError;
+  bool NestedInserts;
+
+  struct Change
+  {
+    QModelIndex Parent;
+    Qt::Orientation Orientation;
+    int Start;
+    int End;
+    
+    int Count;
+    QList<QPersistentModelIndex> Items;
+  };
+
+  QStack<Change> AboutToBeInserted;
+  QStack<Change> AboutToBeRemoved;
+  QList<QPersistentModelIndex> LayoutAboutToBeChanged;
+};
+
+//-----------------------------------------------------------------------------
+ctkModelTesterPrivate::ctkModelTesterPrivate()
+{
+  this->Model = 0;
+  this->ThrowOnError = true;
+  this->NestedInserts = false;
+}
+
+//-----------------------------------------------------------------------------
+ctkModelTester::ctkModelTester(QAbstractItemModel *_model, QObject *_parent)
+  :QObject(_parent)
+{
+  QCTK_INIT_PRIVATE(ctkModelTester);
+  this->setModel(_model);
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::setModel(QAbstractItemModel *_model)
+{
+  QCTK_D(ctkModelTester);
+  if (d->Model)
+    {
+    // disconnect
+    d->Model->disconnect(this);
+    d->AboutToBeInserted.clear();
+    d->AboutToBeRemoved.clear();
+    d->LayoutAboutToBeChanged.clear();
+    }
+  if (_model)
+    {
+    connect(_model, SIGNAL(columnsAboutToBeInserted(const QModelIndex &, int, int)),
+            this, SLOT(onColumnsAboutToBeInserted(const QModelIndex& , int, int)));
+    connect(_model, SIGNAL(columnsAboutToBeRemoved(const QModelIndex &, int, int)),
+            this, SLOT(onColumnsAboutToBeRemoved(const QModelIndex& , int, int)));
+    connect(_model, SIGNAL(columnsInserted(const QModelIndex &, int, int)),
+            this, SLOT(onColumnsInserted(const QModelIndex& , int, int)));
+    connect(_model, SIGNAL(columnsRemoved(const QModelIndex &, int, int)),
+            this, SLOT(onColumnsRemoved(const QModelIndex& , int, int)));
+    connect(_model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
+            this, SLOT(onDataChanged(const QModelIndex& , const QModelIndex &)));
+    connect(_model, SIGNAL(layoutAboutToBeChanged()), this, SLOT(onLayoutAboutToBeChanged()));
+    connect(_model, SIGNAL(layoutChanged()), this, SLOT(onLayoutChanged()));
+    connect(_model, SIGNAL(modelAboutToBeReset()), this, SLOT(onModelAboutToBeReset()));
+    connect(_model, SIGNAL(modelReset()), this, SLOT(onModelReset()));
+    connect(_model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)),
+            this, SLOT(onRowsAboutToBeInserted(const QModelIndex& , int, int)));
+    connect(_model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)),
+            this, SLOT(onRowsAboutToBeRemoved(const QModelIndex& , int, int)));
+    connect(_model, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
+            this, SLOT(onRowsInserted(const QModelIndex& , int, int)));
+    connect(_model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
+            this, SLOT(onRowsRemoved(const QModelIndex& , int, int)));
+    }
+  d->Model = _model;
+  this->testModel();
+}
+
+//-----------------------------------------------------------------------------
+QAbstractItemModel* ctkModelTester::model()const
+{
+  return qctk_d()->Model;
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::setThrowOnError(bool throwException)
+{
+  qctk_d()->ThrowOnError = throwException;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkModelTester::throwOnError()const
+{
+  return qctk_d()->ThrowOnError;
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::setNestedInserts( bool nestedInserts )
+{
+  qctk_d()->NestedInserts = nestedInserts;
+}
+
+//-----------------------------------------------------------------------------
+bool ctkModelTester::nestedInserts()const
+{
+  return qctk_d()->NestedInserts;
+}
+
+//-----------------------------------------------------------------------------
+void  ctkModelTester::test(bool result, const QString& errorString)const
+{
+  if (result)
+    {
+    return;
+    }
+  qDebug() << errorString;
+  if (this->throwOnError())
+    {
+    throw errorString;
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::testModelIndex(const QModelIndex& index)const
+{
+  QCTK_D(const ctkModelTester);
+  if (!index.isValid())
+    {// invalid index
+    this->test(index.model() == 0, "An invalid index can't have a valid model.");
+    this->test(index.model() != d->Model, "An invalid index can't have a valid model.");
+    this->test(index.column() == -1, "An invalid index can't have a valid column.");
+    this->test(index.row() == -1, "An invalid index can't have a valid row.");
+    this->test(index.parent().isValid() == false, "An invalid index can't have a valid row.");
+    this->test(index.row() == -1, "An invalid index can't have a valid row.");
+    for(int i = 0; i < 100; ++i)
+      {
+      this->test(index.sibling(i % 10, i / 10).isValid() == false, "An invalid index can't have valid sibling.");
+      }
+    }
+  else
+    {// valid index
+    this->test(index.model() == d->Model, "A valid index must have a valid model.");
+    this->test(index.column() >= 0, "An valid index can't have an invalid column.");
+    this->test(index.row() >= 0, "An valid index can't have an invalid row.");
+    this->test(index == index.sibling(index.row(), index.column()), "Index's row and/or column is wrong.");
+    }
+  this->testData(index);
+  this->testParent(index);
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::testData(const QModelIndex& index)const
+{
+  if (!index.isValid())
+    {
+    this->test(!index.data(Qt::DisplayRole).isValid(), 
+               QString("An invalid index can't have valid data: %1")
+               .arg(index.data(Qt::DisplayRole).toString()));
+    }
+  else
+    {
+    this->test(index.data(Qt::DisplayRole).isValid(), 
+               QString("A valid index can't have invalid data: %1, %2, %3")
+               .arg(index.row()).arg(index.column()).arg(long(index.internalPointer())));
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::testParent(const QModelIndex& vparent)const
+{
+  QCTK_D(const ctkModelTester);
+  if (!d->Model->hasChildren(vparent))
+    {
+    // it's asking a lot :-)
+    //this->test(d->Model->columnCount(vparent) <= 0, "A parent with no children can't have a columnCount > 0.");
+    this->test(d->Model->rowCount(vparent) <= 0, "A parent with no children can't have a rowCount > 0.");
+    }
+  else
+    {
+    this->test(d->Model->columnCount(vparent) > 0, "A parent with children can't have a columnCount <= 0.");
+    this->test(d->Model->rowCount(vparent) > 0 || d->Model->canFetchMore(vparent), "A parent with children can't have a rowCount <= 0. or if it does, canFetchMore should return true");
+    }
+
+  if (!vparent.isValid())
+    {// otherwise there will be an infinite loop
+    return;
+    }
+  
+  for (int i = 0 ; i < d->Model->rowCount(vparent); ++i)
+    {
+    for (int j = 0; j < d->Model->columnCount(vparent); ++j)
+      {
+      this->test(d->Model->hasIndex(i, j, vparent), "hasIndex should return true for int range {0->rowCount(), 0->columnCount()}");
+      QModelIndex child = vparent.child(i, j);
+      this->test(child.parent() == vparent, "A child's parent can't be different from its parent");
+      this->testModelIndex(child);
+      }
+    }
+}
+//-----------------------------------------------------------------------------
+void ctkModelTester::testPersistentModelIndex(const QPersistentModelIndex& index)const
+{
+  QCTK_D(const ctkModelTester);
+  //qDebug() << "Test persistent Index: " << index ;
+  this->test(index.isValid(), "Persistent model index can't be invalid");
+  // did you forget to call QAbstractItemModel::changePersistentIndex() between 
+  // beginLayoutChanged() and changePersistentIndex() ?
+  QModelIndex modelIndex = d->Model->index(index.row(), index.column(), index.parent());
+  this->test(modelIndex == index, 
+             QString("Persistent index (%1, %2) can't be invalid").arg(index.row()).arg(index.column()));
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::testModel()const
+{
+  QCTK_D(const ctkModelTester);
+  if (d->Model == 0)
+    {
+    return;
+    }
+  for (int i = 0 ; i < d->Model->rowCount(); ++i)
+    {
+    for (int j = 0; j < d->Model->columnCount(); ++j)
+      {
+      this->test(d->Model->hasIndex(i, j), "hasIndex should return true for int range {0->rowCount(), 0->columnCount()}");
+      QModelIndex child = d->Model->index(i, j);
+      this->test(!child.parent().isValid(), "A child's parent can't be different from its parent");
+      this->testModelIndex(child);
+      }
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onColumnsAboutToBeInserted(const QModelIndex & vparent, int start, int end)
+{
+  //qDebug() << "columnsAboutToBeInserted: " << vparent << start << end;
+  this->onItemsAboutToBeInserted(vparent, Qt::Horizontal, start, end);
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onColumnsAboutToBeRemoved(const QModelIndex & vparent, int start, int end)
+{
+  //qDebug() << "columnsAboutToBeRemoved: " << vparent << start << end;
+  this->onItemsAboutToBeRemoved(vparent, Qt::Horizontal, start, end);
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onColumnsInserted(const QModelIndex & vparent, int start, int end)
+{
+  //qDebug() << "columnsInserted: " << vparent << start << end;
+  this->onItemsInserted(vparent, Qt::Horizontal, start, end);
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onColumnsRemoved(const QModelIndex & vparent, int start, int end)
+{
+  //qDebug() << "columnsRemoved: " << vparent << start << end;
+  this->onItemsRemoved(vparent, Qt::Horizontal, start, end);
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight)
+{
+  this->test(topLeft.parent() == bottomRight.parent(), "DataChanged support items with the same parent only");
+  this->test(topLeft.row() >= bottomRight.row(), "topLeft can't have a row lower than bottomRight");
+  this->test(bottomRight.column() >= topLeft.column(), "topLeft can't have a column lower than bottomRight");
+  for (int i = topLeft.row(); i <= bottomRight.row(); ++i)
+    {
+    for (int j = topLeft.column(); j < bottomRight.column(); ++j)
+      {
+      this->test(topLeft.sibling(i,j).isValid(), "Changed data must be valid");
+      // do the test on the indexes here, it's easier to debug than in testModel();
+      this->testModelIndex(topLeft.sibling(i,j));
+      }
+    }
+  this->testModel();
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onHeaderDataChanged(Qt::Orientation orientation, int first, int last)
+{
+  QCTK_D(ctkModelTester);
+  this->test(first <= last, "Changed headers have wrong indexes");
+  switch (orientation)
+    {
+    case Qt::Horizontal:
+      this->test(d->Model->columnCount() > last, "There is no more horizontal headers than columns.");
+      break;
+    case Qt::Vertical:
+      this->test(d->Model->rowCount() > last, "There is no more vertical headers than rows.");
+      break;
+    default:
+      this->test(orientation == Qt::Horizontal || orientation == Qt::Vertical, "Wrong orientation.");
+      break;
+    }
+  this->testModel();
+}
+
+//-----------------------------------------------------------------------------
+QList<QPersistentModelIndex> ctkModelTester::persistentModelIndexes(const QModelIndex& index)const
+{
+  QCTK_D(const ctkModelTester);
+  QList<QPersistentModelIndex> list;
+  for (int i = 0; i < d->Model->rowCount(index); ++i)
+    {
+    for (int j = 0; j < d->Model->columnCount(index); ++j)
+      {
+      QPersistentModelIndex child = d->Model->index(i, j, index);
+      list.append(child);
+      list += this->ctkModelTester::persistentModelIndexes(child);
+      }
+    }
+  return list;
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onLayoutAboutToBeChanged()
+{
+  QCTK_D(ctkModelTester);
+
+  d->LayoutAboutToBeChanged = this->persistentModelIndexes(QModelIndex());
+  this->testModel();
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onLayoutChanged()
+{
+  QCTK_D(ctkModelTester);
+  foreach (const QPersistentModelIndex& index, d->LayoutAboutToBeChanged)
+    {
+    this->testPersistentModelIndex(index);
+    }
+  d->LayoutAboutToBeChanged.clear();
+  this->testModel();
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onModelAboutToBeReset()
+{
+  this->testModel();
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onModelReset()
+{
+  this->testModel();
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onRowsAboutToBeInserted(const QModelIndex &vparent, int start, int end)
+{
+  //qDebug() << "rowsAboutToBeInserted: " << vparent << start << end;
+  this->onItemsAboutToBeInserted(vparent, Qt::Vertical, start, end);
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onRowsAboutToBeRemoved(const QModelIndex &vparent, int start, int end)
+{
+  //qDebug() << "rowsAboutToBeRemoved: " << vparent << start << end;
+  this->onItemsAboutToBeRemoved(vparent, Qt::Vertical, start, end);
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onRowsInserted(const QModelIndex & vparent, int start, int end)
+{
+  //qDebug() << "rowsInserted: " << vparent << start << end;
+  this->onItemsInserted(vparent, Qt::Vertical, start, end);
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onRowsRemoved(const QModelIndex & vparent, int start, int end)
+{
+  //qDebug() << "rowsRemoved: " << vparent << start << end;
+  this->onItemsRemoved(vparent, Qt::Vertical, start, end);
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onItemsAboutToBeInserted(const QModelIndex &vparent, Qt::Orientation orientation, int start, int end)
+{
+  QCTK_D(ctkModelTester);
+  this->test(start <= end, "Start can't be higher than end");
+  //Not sure about that
+  if (!d->NestedInserts)
+    {
+    this->test(d->AboutToBeInserted.size() == 0, "While inserting items, you can't insert other items.");
+    }
+  //Not sure about that
+  this->test(d->AboutToBeRemoved.size() == 0, "While removing items, you can't insert other items.");
+
+  ctkModelTesterPrivate::Change change;
+  change.Parent = vparent;
+  change.Orientation = orientation;
+  change.Start = start;
+  change.End = end;
+  change.Count = (orientation == Qt::Vertical ? d->Model->rowCount(vparent) :d->Model->columnCount(vparent) );
+  change.Items = this->persistentModelIndexes(vparent);
+  d->AboutToBeInserted.push(change);
+  
+  this->testModel();
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onItemsAboutToBeRemoved(const QModelIndex &vparent, Qt::Orientation orientation, int start, int end)
+{
+  QCTK_D(ctkModelTester);
+  this->test(start <= end, "Start can't be higher than end");
+  //Not sure about that
+  this->test(d->AboutToBeInserted.size() == 0, "While inserting items, you can't remove other items.");
+  //Not sure about that
+  this->test(d->AboutToBeRemoved.size() == 0, "While removing items, you can't remove other items.");
+  
+  int count = (orientation == Qt::Vertical ? d->Model->rowCount(vparent) :d->Model->columnCount(vparent) );
+  this->test(start < count, "Item to remove can't be invalid");
+  this->test(end < count, "Item to remove can't be invalid");
+  
+  ctkModelTesterPrivate::Change change;
+  change.Parent = vparent;
+  change.Orientation = orientation;
+  change.Start = start;
+  change.End = end;
+  change.Count = count;
+  for (int i = 0 ; i < count; ++i)
+    {
+    QPersistentModelIndex index;
+    index = (orientation == Qt::Vertical ? d->Model->index(i, 0, vparent) : d->Model->index(0, i, vparent));
+    this->test(index.isValid(), "Index invalid");
+    if (orientation == Qt::Vertical && (index.row() < start || index.row() > end))
+      {
+      change.Items.append(index);
+      }
+    if (orientation == Qt::Horizontal && (index.column() < start || index.column() > end))
+      {
+      change.Items.append(index);
+      }
+    }
+  d->AboutToBeRemoved.push(change);
+
+  this->testModel();
+  //qDebug() << "About to be removed: " << start << " " << end <<vparent << count << change.Items.count();
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onItemsInserted(const QModelIndex & vparent, Qt::Orientation orientation, int start, int end)
+{
+  QCTK_D(ctkModelTester);
+  this->test(start <= end, "Start can't be higher end");
+  this->test(d->AboutToBeInserted.size() != 0, "rowsInserted() has been emitted, but not rowsAboutToBeInserted.");
+  //Not sure about that
+  this->test(d->AboutToBeRemoved.size() == 0, "While removing items, you can't insert other items.");
+
+  ctkModelTesterPrivate::Change change = d->AboutToBeInserted.pop();
+  this->test(change.Parent == vparent, "Parent can't be different");
+  this->test(change.Orientation == Qt::Vertical, "Orientation can't be different");
+  this->test(change.Start == start, "Start can't be different");
+  this->test(change.End == end, "End can't be different");
+  int count =  (orientation == Qt::Vertical ? d->Model->rowCount(vparent) :d->Model->columnCount(vparent) );
+  this->test(change.Count < count, "The new count number can't be lower");
+  this->test(count - change.Count == (end - start + 1) , "The new count number can't be lower");
+  foreach(const QPersistentModelIndex& index, change.Items)
+    {
+    this->testPersistentModelIndex(index);
+    }
+  change.Items.clear();
+  
+  this->testModel();
+}
+
+//-----------------------------------------------------------------------------
+void ctkModelTester::onItemsRemoved(const QModelIndex & vparent, Qt::Orientation orientation, int start, int end)
+{ 
+  QCTK_D(ctkModelTester);
+  this->test(start <= end, "Start can't be higher end");
+  this->test(d->AboutToBeRemoved.size() != 0, "rowsRemoved() has been emitted, but not rowsAboutToBeRemoved.");
+  //Not sure about that
+  this->test(d->AboutToBeInserted.size() == 0, "While inserted items, you can't remove other items.");
+
+  ctkModelTesterPrivate::Change change = d->AboutToBeRemoved.pop();
+  this->test(change.Parent == vparent, "Parent can't be different");
+  this->test(change.Orientation == Qt::Vertical, "Orientation can't be different");
+  this->test(change.Start == start, "Start can't be different");
+  this->test(change.End == end, "End can't be different");
+  int count = (orientation == Qt::Vertical ? d->Model->rowCount(vparent) :d->Model->columnCount(vparent) );
+  this->test(change.Count > count, "The new count number can't be higher");
+  this->test(change.Count - count == (end - start + 1) , "The new count number can't be higher");
+  foreach(const QPersistentModelIndex& index, change.Items)
+    {
+    this->testPersistentModelIndex(index);
+    }
+  change.Items.clear();
+  
+  this->testModel();
+}
+

+ 79 - 0
Libs/Core/ctkModelTester.h

@@ -0,0 +1,79 @@
+/*=========================================================================
+
+  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 __ctkModelTester_h
+#define __ctkModelTester_h
+
+/// QT includes
+#include <QObject> 
+#include <QModelIndex>
+#include <QPersistentModelIndex>
+#include <QList>
+
+/// CTK includes
+#include "ctkPimpl.h"
+#include "CTKCoreExport.h"
+
+class QAbstractItemModel;
+class ctkModelTesterPrivate;
+
+class CTK_CORE_EXPORT ctkModelTester: public QObject
+{
+  Q_OBJECT
+  Q_PROPERTY(bool nestedInserts READ nestedInserts WRITE setNestedInserts);
+public:
+  ctkModelTester(QAbstractItemModel *model, QObject *parent = 0);
+
+  void setModel(QAbstractItemModel* model);
+  QAbstractItemModel* model()const;
+
+  void setThrowOnError(bool throwException);
+  bool throwOnError()const;
+ 
+  void setNestedInserts(bool enable);
+  bool nestedInserts()const;
+
+  virtual void testData(const QModelIndex& index)const;
+  virtual void testModel()const;
+  virtual void testModelIndex(const QModelIndex& index)const;
+  virtual void testParent(const QModelIndex& parent)const;
+  virtual void testPersistentModelIndex(const QPersistentModelIndex& index)const;
+
+protected slots:
+  void onColumnsAboutToBeInserted(const QModelIndex & parent, int start, int end);
+  void onColumnsAboutToBeRemoved(const QModelIndex & parent, int start, int end);
+  void onColumnsInserted(const QModelIndex & parent, int start, int end);
+  void onColumnsRemoved(const QModelIndex & parent, int start, int end);
+  void onDataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
+  void onHeaderDataChanged(Qt::Orientation orientation, int first, int last);
+  void onLayoutAboutToBeChanged();
+  void onLayoutChanged();
+  void onModelAboutToBeReset();
+  void onModelReset();
+  void onRowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
+  void onRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
+  void onRowsInserted(const QModelIndex & parent, int start, int end);
+  void onRowsRemoved(const QModelIndex & parent, int start, int end);
+protected:
+  virtual void onItemsAboutToBeInserted(const QModelIndex& parent, Qt::Orientation, int start, int end);
+  virtual void onItemsAboutToBeRemoved(const QModelIndex& parent, Qt::Orientation, int start, int end);
+  virtual void onItemsInserted(const QModelIndex& parent, Qt::Orientation, int start, int end);
+  virtual void onItemsRemoved(const QModelIndex& parent, Qt::Orientation, int start, int end);
+  QList<QPersistentModelIndex> persistentModelIndexes(const QModelIndex& index)const;
+  virtual void test(bool result, const QString& errorString)const;
+private:
+  QCTK_DECLARE_PRIVATE(ctkModelTester);
+};
+
+#endif

+ 2 - 2
Libs/Core/target_libraries.cmake

@@ -5,6 +5,6 @@
 #
 
 SET(target_libraries
-  ${QT_LIBRARIES}
-  ${QTMOBILITY_QTSERVICEFW_LIBRARY}
+  QT_LIBRARIES
+  QTMOBILITY_QTSERVICEFW_LIBRARIES
   )

+ 24 - 4
Libs/DICOM/Core/Testing/ctkDICOMModelTest1.cxx

@@ -9,28 +9,39 @@
 // ctkDICOMCore includes
 #include "ctkDICOM.h"
 #include "ctkDICOMModel.h"
+#include "ctkModelTester.h"
 
 // STD includes
 #include <iostream>
 
 
 /* Test from build directory:
- ./CTK-build/bin/CTKDICOMCoreCxxTests ctkDICOMModelTest1 ../CTK/Libs/DICOM/Core/Resources/dicom-sample.sql
+ ./CTK-build/bin/CTKDICOMCoreCxxTests ctkDICOMModelTest1 test.db ../CTK/Libs/DICOM/Core/Resources/dicom-sample.sql
 */
 
 int ctkDICOMModelTest1( int argc, char * argv [] )
 {
   QApplication app(argc, argv);
   
-  if (argc <= 1)
+  if (argc <= 2)
     {
     std::cerr << "Warning, no sql file given. Test stops" << std::endl;
+    std::cerr << "Usage: qctkDICOMModelTest1 <scratch.db> <dumpfile.sql>" << std::endl;
     return EXIT_FAILURE;
     }
   
   ctkDICOM myCTK;
-  myCTK.openDatabase( argv[1] );
-  myCTK.initializeDatabase(argv[2]);
+  if (!myCTK.openDatabase( argv[1] ))
+    {
+    std::cerr << "Error when opening the data base file: " << argv[1] 
+              << " error: " << myCTK.GetLastError().toStdString();
+    return EXIT_FAILURE;
+    }
+  if (!myCTK.initializeDatabase(argv[2]))
+    {
+    std::cerr << "Error when initializing the data base: " << argv[2] 
+              << " error: " << myCTK.GetLastError().toStdString();
+    }
     /*
   QSqlQuery toto("SELECT PatientsName as 'Name tt' FROM Patients ORDER BY \"Name tt\" ASC", myCTK.database());
   qDebug() << "toto: " << myCTK.GetLastError() ;
@@ -45,7 +56,16 @@ int ctkDICOMModelTest1( int argc, char * argv [] )
   qDebug() << "tutu: " << tutu.seek(0) << myCTK.GetLastError();
   */
 
+  ctkModelTester tester(0); 
+  tester.setNestedInserts(true);
+  tester.setThrowOnError(false);
   ctkDICOMModel model(0);
+  tester.setModel(&model);
+
+  model.setDatabase(myCTK.database());
+  
+  model.setDatabase(QSqlDatabase());
+ 
   model.setDatabase(myCTK.database());
 
   QTreeView viewer(0);

+ 179 - 36
Libs/DICOM/Core/ctkDICOMModel.cxx

@@ -20,7 +20,7 @@ class ctkDICOMModelPrivate:public qCTKPrivate<ctkDICOMModel>
 {
 public:
   ctkDICOMModelPrivate();
-  ~ctkDICOMModelPrivate();
+  virtual ~ctkDICOMModelPrivate();
   void init();
 
   enum IndexType{
@@ -32,8 +32,13 @@ public:
   };
  
   void fetch(const QModelIndex& index, int limit);
-  Node* createNode(int row, int column, const QModelIndex& parent)const;
+  Node* createNode(int row, const QModelIndex& parent)const;
   Node* nodeFromIndex(const QModelIndex& index)const;
+  //QModelIndexList indexListFromNode(const Node* node)const;
+  //QModelIndexList modelIndexList(Node* node = 0)const;
+  //int childrenCount(Node* node = 0)const;
+  // move it in the Node struct
+  QVariant value(Node* parent, int row, int field)const;
   QVariant value(const QModelIndex& index, int row, int field)const;
   QString  generateQuery(const QString& fields, const QString& table, const QString& conditions = QString())const;
   void updateQueries(Node* node)const;
@@ -45,6 +50,7 @@ public:
 };
 
 //------------------------------------------------------------------------------
+// 1 node per row
 struct Node
 {
   ~Node()
@@ -59,11 +65,10 @@ struct Node
   Node*     Parent;
   QVector<Node*> Children;
   int       Row;
-  int       Column;
   QSqlQuery Query;
   QString   UID;
   int       RowCount;
-  bool      End;
+  bool      AtEnd;
   bool      Fetching;
 };
 
@@ -94,14 +99,83 @@ Node* ctkDICOMModelPrivate::nodeFromIndex(const QModelIndex& index)const
   return index.isValid() ? reinterpret_cast<Node*>(index.internalPointer()) : this->RootNode;
 }
 
+/*
+//------------------------------------------------------------------------------
+QModelIndexList ctkDICOMModelPrivate::indexListFromNode(const Node* node)const
+{
+  QCTK_P(const ctkDICOMModel);
+  Q_ASSERT(node);
+  QModelIndexList indexList;
+  
+  Node* parentNode = node->Parent;
+  if (parentNode == 0)
+    {
+    return indexList;
+    }
+  int field = parentNode->Query.record().indexOf("UID");
+  int row = -1;
+  for (row = 0; row < parentNode->RowCount; ++row)
+    {
+    QString uid = this->value(parentNode, row, field).toString();
+    if (uid == node->UID)
+      {
+      break;
+      }
+    }
+  if (row >= parentNode->RowCount)
+    {
+    return indexList;
+    }
+  for (int column = 0 ; column < this->Headers.size(); ++column)
+    {
+    indexList.append(p->createIndex(row, column, parentNode));
+    }
+  return indexList;
+}
+
+//------------------------------------------------------------------------------
+QModelIndexList ctkDICOMModelPrivate::modelIndexList(Node* node)const
+{
+  QModelIndexList list;
+  if (node == 0)
+    {
+    node = this->RootNode;
+    }
+  foreach(Node* child, node->Children)
+    {
+    list.append(this->indexListFromNode(child));
+    }
+  foreach(Node* child, node->Children)
+    {
+    list.append(this->modelIndexList(child));
+    }
+  return list;
+}
+
+//------------------------------------------------------------------------------
+int ctkDICOMModelPrivate::childrenCount(Node* node)const
+{
+  int count = 0;
+  if (node == 0)
+    {
+    node = this->RootNode;
+    }
+  count += node->Children.size();
+  foreach(Node* child, node->Children)
+    {
+    count += this->childrenCount(child);
+    }
+  return count;
+}
+*/
 //------------------------------------------------------------------------------
-Node* ctkDICOMModelPrivate::createNode(int row, int column, const QModelIndex& parent)const
+Node* ctkDICOMModelPrivate::createNode(int row, const QModelIndex& parent)const
 {
   QCTK_P(const ctkDICOMModel);
   
   Node* node = new Node;
   Node* nodeParent = 0;
-  if (row == -1 || column == -1)
+  if (row == -1)
     {// root node
     node->Type = ctkDICOMModelPrivate::RootType;
     node->Parent = 0;
@@ -110,11 +184,10 @@ Node* ctkDICOMModelPrivate::createNode(int row, int column, const QModelIndex& p
     {
     nodeParent = this->nodeFromIndex(parent); 
     nodeParent->Children.push_back(node);
-    node->Parent = (nodeParent == this->RootNode ? 0: nodeParent);
+    node->Parent = nodeParent;
     node->Type = ctkDICOMModelPrivate::IndexType(nodeParent->Type + 1);
     }
   node->Row = row;
-  node->Column = column;
   if (node->Type != ctkDICOMModelPrivate::RootType)
     {
     int field = nodeParent->Query.record().indexOf("UID");
@@ -122,7 +195,7 @@ Node* ctkDICOMModelPrivate::createNode(int row, int column, const QModelIndex& p
     }
   
   node->RowCount = 0;
-  node->End = false;
+  node->AtEnd = false;
   node->Fetching = false;
 
   this->updateQueries(node);
@@ -138,13 +211,23 @@ QVariant ctkDICOMModelPrivate::value(const QModelIndex& parent, int row, int col
     {      
     const_cast<ctkDICOMModelPrivate *>(this)->fetch(parent, row + 256);
     }
+  return this->value(node, row, column);
+}
 
-  if (!node->Query.seek(row)) 
+//------------------------------------------------------------------------------
+QVariant ctkDICOMModelPrivate::value(Node* parent, int row, int column) const
+{
+  Q_ASSERT(row < parent->RowCount);
+
+  if (!parent->Query.seek(row)) 
     {
-    qDebug() << node->Query.lastError();
+    qDebug() << parent->Query.lastError();
+    Q_ASSERT(parent->Query.seek(row));
     return QVariant();
     }
-  return node->Query.value(column);
+  QVariant res = parent->Query.value(column);
+  Q_ASSERT(res.isValid());
+  return res;
 }
 
 //------------------------------------------------------------------------------
@@ -165,6 +248,7 @@ QString ctkDICOMModelPrivate::generateQuery(const QString& fields, const QString
 //------------------------------------------------------------------------------
 void ctkDICOMModelPrivate::updateQueries(Node* node)const
 {
+  QCTK_P(const ctkDICOMModel);
   // are you kidding me, it should be virtualized here :-)
   QString query;
   switch(node->Type)
@@ -203,7 +287,7 @@ void ctkDICOMModelPrivate::fetch(const QModelIndex& index, int limit)
 {
   QCTK_P(ctkDICOMModel);
   Node* node = this->nodeFromIndex(index);
-  if (node->End || limit <= node->RowCount || node->Fetching/*|| bottom.column() == -1*/)
+  if (node->AtEnd || limit <= node->RowCount || node->Fetching/*|| bottom.column() == -1*/)
     {
     return;
     }
@@ -232,7 +316,7 @@ void ctkDICOMModelPrivate::fetch(const QModelIndex& index, int limit)
       // empty or invalid query
       newRowCount = 0;
       }
-    node->End = true; // this is the end.
+    node->AtEnd = true; // this is the end.
     }
   if (newRowCount > 0 && newRowCount > node->RowCount) 
     {
@@ -265,7 +349,7 @@ bool ctkDICOMModel::canFetchMore ( const QModelIndex & parent ) const
 {
   QCTK_D(const ctkDICOMModel);
   Node* node = d->nodeFromIndex(parent);
-  return !node->End;
+  return !node->AtEnd;
 }
 
 //------------------------------------------------------------------------------
@@ -273,7 +357,7 @@ int ctkDICOMModel::columnCount ( const QModelIndex & _parent ) const
 {
   QCTK_D(const ctkDICOMModel);
   Q_UNUSED(_parent);
-  return d->Headers.size();
+  return d->RootNode != 0 ? d->Headers.size() : 0;
 }
 
 //------------------------------------------------------------------------------
@@ -285,9 +369,8 @@ QVariant ctkDICOMModel::data ( const QModelIndex & index, int role ) const
     return QVariant();
     }
   QModelIndex indexParent = this->parent(index);
-  Node* node = d->nodeFromIndex(indexParent);
-  Q_ASSERT(node->Row == indexParent.row());
-  if (index.row() >= node->RowCount)
+  Node* parentNode = d->nodeFromIndex(indexParent);
+  if (index.row() >= parentNode->RowCount)
     {      
     const_cast<ctkDICOMModelPrivate *>(d)->fetch(index, index.row());
     }
@@ -298,10 +381,10 @@ QVariant ctkDICOMModel::data ( const QModelIndex & index, int role ) const
     return QVariant();
     }
     */
-  int field = node->Query.record().indexOf(d->Headers[index.column()]);
+  int field = parentNode->Query.record().indexOf(d->Headers[index.column()]);
   if (field < 0)
     {
-    return QVariant();
+    return QString();
     }
   return d->value(indexParent, index.row(), field);
   //return node->Query.value(field);
@@ -325,8 +408,21 @@ Qt::ItemFlags ctkDICOMModel::flags ( const QModelIndex & index ) const
 bool ctkDICOMModel::hasChildren ( const QModelIndex & parent ) const
 {
   QCTK_D(const ctkDICOMModel);
+  if (parent.column() > 0)
+    {
+    return false;
+    }
   Node* node = d->nodeFromIndex(parent);
-  return node->RowCount > 0 || (!node->End && node->Query.seek(0));
+  if (!node)
+    {
+    return false;
+    }
+  if (node->RowCount == 0 && !node->AtEnd)
+    {
+    //const_cast<qCTKDCMTKModelPrivate*>(d)->fetch(parent, 1);
+    return node->Query.seek(0);
+    }
+  return node->RowCount > 0;
 }
 
 //------------------------------------------------------------------------------
@@ -350,12 +446,17 @@ QVariant ctkDICOMModel::headerData(int section, Qt::Orientation orientation, int
 QModelIndex ctkDICOMModel::index ( int row, int column, const QModelIndex & parent ) const
 {
   QCTK_D(const ctkDICOMModel);
+  if (d->RootNode == 0 || parent.column() > 0)
+    {
+    return QModelIndex();
+    }
   Node* parentNode = d->nodeFromIndex(parent);
+  int field = parentNode->Query.record().indexOf("UID");
+  QString uid = d->value(parent, row, field).toString();
   Node* node = 0;
   foreach(Node* tmpNode, parentNode->Children)
     {
-    if (tmpNode->Row == row && 
-        tmpNode->Column == column)
+    if (tmpNode->UID == uid)
       {
       node = tmpNode;
       break;
@@ -363,7 +464,7 @@ QModelIndex ctkDICOMModel::index ( int row, int column, const QModelIndex & pare
     }
   if (node == 0)
     {
-    node = d->createNode(row, column, parent);
+    node = d->createNode(row, parent);
     }
   return this->createIndex(row, column, node);
 }
@@ -373,21 +474,47 @@ QModelIndex ctkDICOMModel::parent ( const QModelIndex & index ) const
 {
   QCTK_D(const ctkDICOMModel);
   Node* node = d->nodeFromIndex(index);
-  if (node == 0 || node->Parent == 0)
+  Q_ASSERT(node);
+  Node* parentNode = node->Parent;
+  if (parentNode == 0)
+    {// node is root
+    return QModelIndex();
+    }
+  return parentNode == d->RootNode ? QModelIndex() : this->createIndex(parentNode->Row, 0, parentNode);
+  /* need to recalculate the parent row
+  Node* greatParentNode = parentNode->Parent;
+  if (greatParentNode == 0)
     {
     return QModelIndex();
     }
-  return this->createIndex(node->Parent->Row, node->Parent->Column, node->Parent);
+  int field = greatParentNode->Query.record().indexOf("UID");
+  int row = -1;
+  for (row = 0; row < greatParentNode->RowCount; ++row)
+    {
+    QString uid = d->value(greatParentNode, row, field).toString();
+    if (uid == parentNode->UID)
+      {
+      break;
+      }
+    }
+  Q_ASSERT(row < greatParentNode->RowCount);
+  return this->createIndex(row, 0, parentNode);
+  */
 }
 
 //------------------------------------------------------------------------------
 int ctkDICOMModel::rowCount ( const QModelIndex & parent ) const
 {
   QCTK_D(const ctkDICOMModel);
+  if (d->RootNode == 0 || parent.column() > 0)
+    {
+    return 0;
+    }
   Node* node = d->nodeFromIndex(parent);
-  if (node->RowCount == 0 && node->End)
+  Q_ASSERT(node);
+  if (node->RowCount == 0 && !node->AtEnd)
     {
-    const_cast<ctkDICOMModelPrivate*>(d)->fetch(parent, 256);
+    //const_cast<ctkDICOMModelPrivate*>(d)->fetch(parent, 256);
     }
   return node->RowCount;
 }
@@ -405,26 +532,24 @@ void ctkDICOMModel::setDatabase(const QSqlDatabase &db)
 
   if (d->DataBase.tables().empty())
     {
-    Q_ASSERT(d->DataBase.isOpen());
+    //Q_ASSERT(d->DataBase.isOpen());
+    this->endResetModel();
     return;
     }
     
-  d->RootNode = d->createNode(-1, -1, QModelIndex());
+  d->RootNode = d->createNode(-1, QModelIndex());
   
   this->endResetModel();
 
+  // TODO, use hasQuerySize everywhere, not only in setDataBase()
   bool hasQuerySize = d->RootNode->Query.driver()->hasFeature(QSqlDriver::QuerySize);
   if (hasQuerySize && d->RootNode->Query.size() > 0) 
     {
     int newRowCount= d->RootNode->Query.size();
     beginInsertRows(QModelIndex(), 0, qMax(0, newRowCount - 1));
     d->RootNode->RowCount = newRowCount;
-    d->RootNode->End = true;
+    d->RootNode->AtEnd = true;
     endInsertRows();
-    } 
-  else
-    {
-    d->RootNode->RowCount = 0;
     }
   d->fetch(QModelIndex(), 256);
 }
@@ -433,12 +558,30 @@ void ctkDICOMModel::setDatabase(const QSqlDatabase &db)
 void ctkDICOMModel::sort(int column, Qt::SortOrder order)
 {
   QCTK_D(ctkDICOMModel);
+  /* The following would work if there is no fetch involved.
+     ORDER BY doesn't just apply on the fetched item. By sorting
+     new items can show up in the model, and we need to be more
+     careful
   emit layoutAboutToBeChanged();
+  QModelIndexList oldIndexList = d->modelIndexList();
   d->Sort = QString("\"%1\" %2")
     .arg(d->Headers[column])
     .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
   d->updateQueries(d->RootNode);
+  QModelIndexList newIndexList = d->modelIndexList();
+  Q_ASSERT(oldIndexList.count() == newIndexList.count());
+  this->changePersistentIndexList(oldIndexList, newIndexList);
   emit layoutChanged();
+  */
+  this->beginResetModel();
+  delete d->RootNode;
+  d->RootNode = 0;
+  d->Sort = QString("\"%1\" %2")
+    .arg(d->Headers[column])
+    .arg(order == Qt::AscendingOrder ? "ASC" : "DESC");
+  d->RootNode = d->createNode(-1, QModelIndex());
+  
+  this->endResetModel();
 }
 
 //------------------------------------------------------------------------------

+ 3 - 0
Libs/DICOM/Core/ctkDICOMModel.h

@@ -25,12 +25,15 @@ public:
   virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
   virtual void fetchMore ( const QModelIndex & parent );
   virtual Qt::ItemFlags flags ( const QModelIndex & index ) const;
+  // can return true even if rowCount returns 0, you should use canFetchMore/fetchMore to populate
+  // the children.
   virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const;
   virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole)const;
   virtual QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const;
   virtual QModelIndex parent ( const QModelIndex & index ) const;
   virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const;
   virtual bool setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role = Qt::EditRole );
+  // Sorting resets the model because fetched/unfetched items could disappear/appear respectively.
   virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
 
 private:

+ 1 - 1
Libs/DICOM/Core/target_libraries.cmake

@@ -6,5 +6,5 @@
 
 SET(target_libraries
   CTKCore
-  ${DCMTK_LIBRARIES}
+  DCMTK_LIBRARIES
   )

+ 64 - 0
Libs/Visualization/XIP/CMakeLists.txt

@@ -0,0 +1,64 @@
+PROJECT(CTKVisualizationXIP)
+
+#
+# 3rd party dependencies
+#
+
+# FIND_PACKAGE(XIP)
+# IF(NOT XIP_FOUND)
+#   MESSAGE(FATAL_ERROR "error: XIP package is required to build ${PROJECT_NAME}")
+# ENDIF()
+
+#
+# See CTK/CMake/ctkMacroBuildQtLib.cmake for details
+#
+
+SET(KIT_export_directive "CTK_VISUALIZATION_XIP_EXPORT")
+
+# Additional directories to include
+SET(KIT_include_directories
+  #${DCMTK_INCLUDE_DIR}
+  )
+  
+# Source files
+SET(KIT_SRCS
+  ctkXIPAdaptor.cxx
+  ctkXIPAdaptor.h
+  )
+
+# Headers that should run through moc
+SET(KIT_MOC_SRCS
+  ctkXIPAdaptor.h
+  )
+
+# UI files
+SET(KIT_UI_FORMS
+)
+
+# Resources
+SET(KIT_resources
+)
+
+# Target libraries - See CMake/ctkMacroGetTargetLibraries.cmake
+# The following macro will read the target libraries from the file 'target_libraries.cmake'
+ctkMacroGetTargetLibraries(KIT_target_libraries)
+
+ctkMacroBuildQtLib(
+  NAME ${PROJECT_NAME}
+  EXPORT_DIRECTIVE ${KIT_export_directive}
+  INCLUDE_DIRECTORIES ${KIT_include_directories}
+  SRCS ${KIT_SRCS}
+  MOC_SRCS ${KIT_MOC_SRCS}
+  UI_FORMS ${KIT_UI_FORMS}
+  TARGET_LIBRARIES ${KIT_target_libraries}
+  RESOURCES ${KIT_resources}
+  LIBRARY_TYPE ${CTK_LIBRARY_MODE}
+  )
+
+# Plugins
+#ADD_SUBDIRECTORY(Plugins)
+
+# Testing
+IF(BUILD_TESTING)
+  #ADD_SUBDIRECTORY(Testing)
+ENDIF(BUILD_TESTING)

+ 35 - 0
Libs/Visualization/XIP/ctkXIPAdaptor.cxx

@@ -0,0 +1,35 @@
+
+// Qt includes
+#include <QDebug>
+
+// ctkXIPAdaptor includes
+#include "ctkXIPAdaptor.h"
+
+//----------------------------------------------------------------------------
+class ctkXIPAdaptorPrivate: public qCTKPrivate<ctkXIPAdaptor>
+{
+public:
+  ctkXIPAdaptorPrivate();
+};
+
+//----------------------------------------------------------------------------
+// ctkXIPAdaptorPrivate methods
+
+//------------------------------------------------------------------------------
+ctkXIPAdaptorPrivate::ctkXIPAdaptorPrivate()
+{
+}
+
+//----------------------------------------------------------------------------
+// ctkXIPAdaptorWidget methods
+
+//------------------------------------------------------------------------------
+ctkXIPAdaptor::ctkXIPAdaptor(QObject* _parent): Superclass(_parent)
+{
+  QCTK_INIT_PRIVATE(ctkXIPAdaptor);
+}
+
+//----------------------------------------------------------------------------
+ctkXIPAdaptor::~ctkXIPAdaptor()
+{
+}

+ 25 - 0
Libs/Visualization/XIP/ctkXIPAdaptor.h

@@ -0,0 +1,25 @@
+#ifndef __ctkXIPAdaptor_h
+#define __ctkXIPAdaptor_h
+
+// QT includes 
+#include <QObject>
+
+// CTK includes
+#include <ctkPimpl.h>
+
+#include "CTKVisualizationXIPExport.h"
+
+class ctkXIPAdaptorPrivate;
+class CTK_VISUALIZATION_XIP_EXPORT ctkXIPAdaptor : public QObject
+{
+  Q_OBJECT
+public:
+  typedef QObject Superclass;
+  explicit ctkXIPAdaptor(QObject* parent = 0);
+  virtual ~ctkXIPAdaptor();
+  
+private:
+  QCTK_DECLARE_PRIVATE(ctkXIPAdaptor);
+};
+
+#endif

+ 12 - 0
Libs/Visualization/XIP/ctk_library_options.cmake

@@ -0,0 +1,12 @@
+#
+# See CMake/ctkMacroAddCtkLibraryOptions.cmake
+# 
+# This file should list of options available for considered CTK library
+# For example: MYOPT1:OFF MYOPT2:ON
+# 
+
+SET(ctk_library_options
+  VTK:ON
+  ITK:OFF
+  )
+  

+ 9 - 0
Libs/Visualization/XIP/target_libraries.cmake

@@ -0,0 +1,9 @@
+#
+# See CMake/ctkMacroGetTargetLibraries.cmake
+# 
+# This file should list the libraries required to build the current CTK libraries
+# 
+
+SET(target_libraries
+  CTKCore
+  )

+ 131 - 80
SuperBuild.cmake

@@ -4,9 +4,9 @@ SET(cmake_version_required_dash "2-8")
 CMAKE_MINIMUM_REQUIRED(VERSION ${cmake_version_required})
 
 # 
-# CTK_QMAKE_EXECUTABLE
 # CTK_KWSTYLE_EXECUTABLE
-# CTK_DCMTK_DIR
+# DCMTK_DIR
+# QT_QMAKE_EXECUTABLE
 #
 
 #-----------------------------------------------------------------------------
@@ -46,15 +46,24 @@ set(sep "^^")
 SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/CMake)
 
 #-----------------------------------------------------------------------------
+# Initialize NON_CTK_DEPENDENCIES variable
+#
+# Using the variable ALL_TARGET_LIBRARIES initialized in the main CMakeLists.txt with the help
+# of the macro ctkMacroGetAllTargetLibraries, let's get the list of all Non-CTK dependencies.
+# NON_CTK_DEPENDENCIES is expected by the macro ctkMacroShouldAddExternalProject
+ctkMacroGetAllNonCTKTargetLibraries("${ALL_TARGET_LIBRARIES}" NON_CTK_DEPENDENCIES)
+#MESSAGE(STATUS NON_CTK_DEPENDENCIES:${NON_CTK_DEPENDENCIES})
+
+#-----------------------------------------------------------------------------
 # Qt is expected to be setup by CTK/CMakeLists.txt just before it includes the SuperBuild script
 #
 
 #-----------------------------------------------------------------------------
 # KWStyle
 #
-SET (kwstyle_DEPENDS)
-IF (CTK_USE_KWSTYLE)
-  IF (NOT DEFINED CTK_KWSTYLE_EXECUTABLE)
+SET(kwstyle_DEPENDS)
+IF(CTK_USE_KWSTYLE)
+  IF(NOT DEFINED CTK_KWSTYLE_EXECUTABLE)
     SET(proj KWStyle-CVSHEAD)
     SET(kwstyle_DEPENDS ${proj})
     ExternalProject_Add(${proj}
@@ -72,111 +81,142 @@ ENDIF()
 #-----------------------------------------------------------------------------
 # PythonQt
 #
-SET (PythonQt_DEPENDS)
-# IF ()
+SET(PythonQt_DEPENDS)
+ctkMacroShouldAddExternalProject(PYTHONQT_LIBRARIES add_project)
+IF(${add_project})
 #   SET(proj PythonQt)
 #   SET(PythonQt_DEPENDS ${proj})
 #   ExternalProject_Add(${proj}
 #       SVN_REPOSITORY "https://pythonqt.svn.sourceforge.net/svnroot/pythonqt/trunk"
 #       CMAKE_GENERATOR ${gen}
 #       PATCH_COMMAND ${CMAKE_COMMAND} -P ${pythonqt_patch_script}
+#       BUILD_COMMAND ""
 #       CMAKE_ARGS
 #         ${ep_common_args}
 #         -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
 #         #${vtk_PYTHON_ARGS}
 #       INSTALL_COMMAND "")
-# ENDIF()
+ENDIF()
     
 #-----------------------------------------------------------------------------
 # Utilities/DCMTK
 #
-SET (DCMTK_DEPENDS)
-IF (NOT DEFINED CTK_DCMTK_DIR)
-  SET(proj DCMTK)
-  SET(DCMTK_DEPENDS ${proj})
+SET(DCMTK_DEPENDS)
+ctkMacroShouldAddExternalProject(DCMTK_LIBRARIES add_project)
+IF(${add_project})
+  IF(NOT DEFINED DCMTK_DIR)
+    SET(proj DCMTK)
+#     MESSAGE(STATUS "Adding project:${proj}")
+    SET(DCMTK_DEPENDS ${proj})
+    ExternalProject_Add(${proj}
+        DOWNLOAD_COMMAND ""
+        CMAKE_GENERATOR ${gen}
+        SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/${proj}
+        CMAKE_ARGS
+          ${ep_common_args}
+          -DDCMTK_BUILD_APPS:BOOL=ON # Build also dmctk tools (movescu, storescp, ...)
+        )
+    SET(DCMTK_DIR ${ep_install_dir})
+  ENDIF()
+ENDIF()
+
+#-----------------------------------------------------------------------------
+# Utilities/ZMQ
+#
+SET(ZMQ_DEPENDS)
+ctkMacroShouldAddExternalProject(ZMQ_LIBRARIES add_project)
+IF(${add_project})
+  SET(proj ZMQ)
+#   MESSAGE(STATUS "Adding project:${proj}")
+  SET(ZMQ_DEPENDS ${proj})
   ExternalProject_Add(${proj}
       DOWNLOAD_COMMAND ""
+      INSTALL_COMMAND ""
       CMAKE_GENERATOR ${gen}
-      SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/${proj}
+      SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ZMQ
       CMAKE_ARGS
         ${ep_common_args}
-        -DDCMTK_BUILD_APPS:BOOL=ON # Build also dmctk tools (movescu, storescp, ...)
       )
-  SET(CTK_DCMTK_DIR ${ep_install_dir})
 ENDIF()
 
 #-----------------------------------------------------------------------------
-# Utilities/ZMQ
-#
-SET(proj ZMQ)
-ExternalProject_Add(${proj}
-    DOWNLOAD_COMMAND ""
-    INSTALL_COMMAND ""
-    CMAKE_GENERATOR ${gen}
-    SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ZMQ
-    CMAKE_ARGS
-      ${ep_common_args}
-    )
-
-#-----------------------------------------------------------------------------
 # QtMobility
 #
-SET(proj QtMobility)
+SET(QtMobility_DEPENDS)
+ctkMacroShouldAddExternalProject(QTMOBILITY_QTSERVICEFW_LIBRARIES add_project)
+IF(${add_project})
+  SET(proj QtMobility)
+#   MESSAGE(STATUS "Adding project:${proj}")
+  SET(QtMobility_DEPENDS ${proj})
+  
+  # Configure patch script
+  SET(qtmobility_src_dir ${ep_source_dir}/${proj})
+  SET(qtmobility_patch_dir ${CTK_SOURCE_DIR}/Utilities/QtMobility/)
+  SET(qtmobility_configured_patch_dir ${CTK_BINARY_DIR}/Utilities/QtMobility/)
+  SET(qtmobility_patchscript
+    ${CTK_BINARY_DIR}/Utilities/QtMobility/QtMobilityBeta1-patch.cmake)
+  CONFIGURE_FILE(
+    ${CTK_SOURCE_DIR}/Utilities/QtMobility/QtMobilityBeta1-patch.cmake.in
+    ${qtmobility_patchscript} @ONLY)
 
-# Configure patch script
-SET(qtmobility_src_dir ${ep_source_dir}/${proj})
-SET(qtmobility_patch_dir ${CTK_SOURCE_DIR}/Utilities/QtMobility/)
-SET(qtmobility_configured_patch_dir ${CTK_BINARY_DIR}/Utilities/QtMobility/)
-SET(qtmobility_patchscript
-  ${CTK_BINARY_DIR}/Utilities/QtMobility/QtMobilityBeta1-patch.cmake)
-CONFIGURE_FILE(
-  ${CTK_SOURCE_DIR}/Utilities/QtMobility/QtMobilityBeta1-patch.cmake.in
-  ${qtmobility_patchscript} @ONLY)
-
-# Define configure options
-SET(qtmobility_modules "serviceframework")
-SET(qtmobility_build_type "release")
-IF(UNIX)
- IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
-   SET(qtmobility_build_type "debug")
- ENDIF()
-ELSEIF(NOT ${CMAKE_CFG_INTDIR} STREQUAL "Release")
- SET(qtmobility_build_type "debug")
-ENDIf()
+  # Define configure options
+  SET(qtmobility_modules "serviceframework")
+  SET(qtmobility_build_type "release")
+  IF(UNIX)
+  IF(CMAKE_BUILD_TYPE STREQUAL "Debug")
+    SET(qtmobility_build_type "debug")
+  ENDIF()
+  ELSEIF(NOT ${CMAKE_CFG_INTDIR} STREQUAL "Release")
+  SET(qtmobility_build_type "debug")
+  ENDIf()
 
-ExternalProject_Add(${proj}
-   URL "http://get.qt.nokia.com/qt/solutions/qt-mobility-src-1.0.0-beta1.tar.gz"
-   CONFIGURE_COMMAND <SOURCE_DIR>/configure -${qtmobility_build_type} -libdir ${CMAKE_BINARY_DIR}/CTK-build/bin -no-docs -modules ${qtmobility_modules}
-   PATCH_COMMAND ${CMAKE_COMMAND} -P ${qtmobility_patchscript}
-   BUILD_IN_SOURCE 1
-   )
+  ExternalProject_Add(${proj}
+    URL "http://get.qt.nokia.com/qt/solutions/qt-mobility-src-1.0.0-beta1.tar.gz"
+    CONFIGURE_COMMAND <SOURCE_DIR>/configure -${qtmobility_build_type} -libdir ${CMAKE_BINARY_DIR}/CTK-build/bin -no-docs -modules ${qtmobility_modules}
+    PATCH_COMMAND ${CMAKE_COMMAND} -P ${qtmobility_patchscript}
+    BUILD_IN_SOURCE 1
+    )
+ENDIF()
 
 #-----------------------------------------------------------------------------
 # Utilities/OpenIGTLink
 #
-SET(proj OpenIGTLink)
-ExternalProject_Add(${proj}
-    SVN_REPOSITORY "http://svn.na-mic.org/NAMICSandBox/trunk/OpenIGTLink"
+SET (OpenIGTLink_DEPENDS)
+ctkMacroShouldAddExternalProject(OpenIGTLink_LIBRARIES add_project)
+IF(${add_project})
+  IF(NOT DEFINED OpenIGTLink_DIR)
+    SET(proj OpenIGTLink)
+  #   MESSAGE(STATUS "Adding project:${proj}")
+    SET(OpenIGTLink_DEPENDS ${proj})
+    ExternalProject_Add(${proj}
+        SVN_REPOSITORY "http://svn.na-mic.org/NAMICSandBox/trunk/OpenIGTLink"
+        INSTALL_COMMAND ""
+        CMAKE_GENERATOR ${gen}
+        CMAKE_ARGS
+          ${ep_common_args}
+        )
+    SET(OpenIGTLink_DIR ${ep_build_dir}/${proj})
+  ENDIF()
+ENDIF()
+
+#-----------------------------------------------------------------------------
+# XIP
+#
+SET (XIP_DEPENDS)
+ctkMacroShouldAddExternalProject(XIP_LIBRARIES add_project)
+IF(${add_project})
+  SET(proj XIP)
+#   MESSAGE(STATUS "Adding project:${proj}")
+  SET(XIP_DEPENDS ${proj})
+  ExternalProject_Add(${proj}
+    SVN_REPOSITORY "https://collab01a.scr.siemens.com/svn/xip/releases/latest"
+    SVN_USERNAME "anonymous"
     INSTALL_COMMAND ""
     CMAKE_GENERATOR ${gen}
     CMAKE_ARGS
       ${ep_common_args}
     )
-
-#-----------------------------------------------------------------------------
-# XIP
-#
-# SET(proj XIP)
-# SET(url https://collab01a.scr.siemens.com/svn/xip/releases/latest)
-# ExternalProject_Add(${proj}
-#    DOWNLOAD_COMMAND "${CMAKE_COMMAND} -E ${SVNCOMMAND} checkout ${url} ${ep_source_dir}/${proj} --username=anonymous "
-#    UPDATE_COMMAND ""
-#    #SVN_REPOSITORY "https://anonymous@collab01a.scr.siemens.com/svn/xip/releases/latest"
-#    INSTALL_COMMAND ""
-#    CMAKE_GENERATOR ${gen}
-#    CMAKE_ARGS
-#      ${ep_common_args}
-#    )
+ENDIF()
    
 #-----------------------------------------------------------------------------
 # CTK Utilities
@@ -188,13 +228,15 @@ ExternalProject_Add(${proj}
   BUILD_COMMAND ""
   INSTALL_COMMAND ""
   DEPENDS
+    # Mandatory dependencies
+    ${QtMobility_DEPENDS}
+    # Optionnal dependencies
     ${kwstyle_DEPENDS}
     ${DCMTK_DEPENDS}
     ${PythonQt_DEPENDS}
-    ZMQ
-    OpenIGTLink
-#     XIP
-    QtMobility
+    ${ZMQ_DEPENDS}
+    ${OpenIGTLink_DEPENDS}
+    ${XIP_DEPENDS}
 )
 
 #-----------------------------------------------------------------------------
@@ -256,9 +298,9 @@ ExternalProject_Add(${proj}
     -DCTK_SUPERBUILD:BOOL=OFF
     -DCMAKE_INSTALL_PREFIX:PATH=${ep_install_dir}
     -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
-    -DCTK_QMAKE_EXECUTABLE:FILEPATH=${CTK_QMAKE_EXECUTABLE}
+    -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}
     -DCTK_KWSTYLE_EXECUTABLE:FILEPATH=${CTK_KWSTYLE_EXECUTABLE}
-    -DDCMTK_DIR=${CTK_DCMTK_DIR} # FindDCMTK expects DCMTK_DIR
+    -DDCMTK_DIR=${DCMTK_DIR} # FindDCMTK expects DCMTK_DIR
   SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
   BINARY_DIR ${CMAKE_BINARY_DIR}/CTK-build
   BUILD_COMMAND ""
@@ -271,7 +313,7 @@ ExternalProject_Add(${proj}
 #-----------------------------------------------------------------------------
 # CTK
 #
-set(proj CTK-build)
+SET(proj CTK-build)
 ExternalProject_Add(${proj}
   DOWNLOAD_COMMAND ""
   CMAKE_GENERATOR ${gen}
@@ -282,3 +324,12 @@ ExternalProject_Add(${proj}
   DEPENDS
     "CTK-Configure"
   )
+
+#-----------------------------------------------------------------------------
+# Custom target allowing to drive the build of CTK project itself
+#
+ADD_CUSTOM_TARGET(CTK
+  COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/CTK-build
+  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CTK-build
+  )
+ 

+ 0 - 9
Utilities/CMake/FindDCMTK.cmake

@@ -36,15 +36,6 @@ IF( NOT DCMTK_FOUND )
   MARK_AS_ADVANCED( DCMTK_DIR )
 ENDIF( NOT DCMTK_FOUND )
 
-# Convenient debug macro
-SET(debug TRUE)
-MACRO(findDcmtkDebug cmake_var)
-  IF(debug)
-    MESSAGE(STATUS ${cmake_var}:"${${cmake_var}}")
-  ENDIF()
-ENDMACRO()
-
-
 FIND_PATH( DCMTK_config_INCLUDE_DIR osconfig.h
   ${DCMTK_DIR}/config/include
   ${DCMTK_DIR}/config

+ 88 - 0
Utilities/CMake/FindOpenIGTLink.cmake

@@ -0,0 +1,88 @@
+# - Find an OpenIGTLink installation or build tree.
+
+# When OpenIGTLink is found, the OpenIGTLinkConfig.cmake file is sourced to setup the
+# location and configuration of OpenIGTLink.  Please read this file, or
+# OpenIGTLinkConfig.cmake.in from the OpenIGTLink source tree for the full list of
+# definitions.  Of particular interest is OpenIGTLink_USE_FILE, a CMake source file
+# that can be included to set the include directories, library directories,
+# and preprocessor macros.  In addition to the variables read from
+# OpenIGTLinkConfig.cmake, this find module also defines
+#  OpenIGTLink_DIR  - The directory containing OpenIGTLinkConfig.cmake.  
+#             This is either the root of the build tree, 
+#             or the lib/InsightToolkit directory.  
+#             This is the only cache entry.
+#   
+#  OpenIGTLink_FOUND - Whether OpenIGTLink was found.  If this is true, 
+#              OpenIGTLink_DIR is okay.
+#
+#  USE_OpenIGTLink_FILE - The full path to the UseOpenIGTLink.cmake file.  
+#                 This is provided for backward 
+#                 compatability.  Use OpenIGTLink_USE_FILE
+#                 instead.
+
+
+SET(OpenIGTLink_DIR_STRING "directory containing OpenIGTLinkConfig.cmake.  This is either the root of the build tree, or PREFIX/lib/igtl for an installation.")
+
+# Search only if the location is not already known.
+IF(NOT OpenIGTLink_DIR)
+  # Get the system search path as a list.
+  IF(UNIX)
+    STRING(REGEX MATCHALL "[^:]+" OpenIGTLink_DIR_SEARCH1 "$ENV{PATH}")
+  ELSE(UNIX)
+    STRING(REGEX REPLACE "\\\\" "/" OpenIGTLink_DIR_SEARCH1 "$ENV{PATH}")
+  ENDIF(UNIX)
+  STRING(REGEX REPLACE "/;" ";" OpenIGTLink_DIR_SEARCH2 ${OpenIGTLink_DIR_SEARCH1})
+
+  # Construct a set of paths relative to the system search path.
+  SET(OpenIGTLink_DIR_SEARCH "")
+  FOREACH(dir ${OpenIGTLink_DIR_SEARCH2})
+    SET(OpenIGTLink_DIR_SEARCH ${OpenIGTLink_DIR_SEARCH} "${dir}/../lib/igtl")
+  ENDFOREACH(dir)
+
+  #
+  # Look for an installation or build tree.
+  #
+  FIND_PATH(OpenIGTLink_DIR OpenIGTLinkConfig.cmake
+    # Look for an environment variable OpenIGTLink_DIR.
+    $ENV{OpenIGTLink_DIR}
+
+    # Look in places relative to the system executable search path.
+    ${OpenIGTLink_DIR_SEARCH}
+
+    # Look in standard UNIX install locations.
+    /usr/local/lib/igtl
+    /usr/lib/igtl
+
+    # Read from the CMakeSetup registry entries.  It is likely that
+    # OpenIGTLink will have been recently built.
+    [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild1]
+    [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild2]
+    [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild3]
+    [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild4]
+    [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild5]
+    [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild6]
+    [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild7]
+    [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild8]
+    [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild9]
+    [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild10]
+
+    # Help the user find it if we cannot.
+    DOC "The ${OpenIGTLink_DIR_STRING}"
+  )
+ENDIF(NOT OpenIGTLink_DIR)
+
+# If OpenIGTLink was found, load the configuration file to get the rest of the
+# settings.
+IF(OpenIGTLink_DIR)
+  SET(OpenIGTLink_FOUND 1)
+  INCLUDE(${OpenIGTLink_DIR}/OpenIGTLinkConfig.cmake)
+
+  # Set USE_OpenIGTLink_FILE for backward-compatability.
+  SET(USE_OpenIGTLink_FILE ${OpenIGTLink_USE_FILE})
+ELSE(OpenIGTLink_DIR)
+  SET(OpenIGTLink_FOUND 0)
+  IF(OpenIGTLink_FIND_REQUIRED)
+    MESSAGE(FATAL_ERROR "Please set OpenIGTLink_DIR to the ${OpenIGTLink_DIR_STRING}")
+  ENDIF(OpenIGTLink_FIND_REQUIRED)
+ENDIF(OpenIGTLink_DIR)
+

+ 24 - 0
Utilities/CMake/FindPythonQt.cmake

@@ -0,0 +1,24 @@
+# Find PythonQt
+#
+# Sets PYTHONQT_FOUND, PYTHONQT_INCLUDE_DIR, PYTHONQT_LIBRARY, PYTHONQT_LIBRARIES
+#
+
+FIND_PATH(PYTHONQT_INSTALL_DIR include/PythonQt/PythonQt.h DOC "Directory where PythonQt was installed.")
+FIND_PATH(PYTHONQT_INCLUDE_DIR PythonQt.h "${PYTHONQT_INSTALL_DIR}/include/PythonQt" DOC "Path to the PythonQt include directory")
+FIND_LIBRARY(PYTHONQT_LIBRARY PythonQt PATHS "${PYTHONQT_INSTALL_DIR}/lib" DOC "The PythonQt library.")
+
+MARK_AS_ADVANCED(PYTHONQT_INSTALL_DIR)
+MARK_AS_ADVANCED(PYTHONQT_INCLUDE_DIR)
+MARK_AS_ADVANCED(PYTHONQT_LIBRARY)
+
+# On linux, also find libutil
+IF(NOT WIN32)
+  FIND_LIBRARY(PYTHONQT_LIBUTIL util)
+  MARK_AS_ADVANCED(PYTHONQT_LIBUTIL)
+ENDIF()
+
+SET(PYTHONQT_FOUND 0)
+IF(PYTHONQT_INCLUDE_DIR AND PYTHONQT_LIBRARY)
+  SET(PYTHONQT_FOUND 1)
+  SET(PYTHONQT_LIBRARIES ${PYTHONQT_LIBRARY} ${PYTHONQT_LIBUTIL})
+ENDIF()

+ 40 - 0
Utilities/QtMobility/QtMobilityBeta1-apple.patch

@@ -0,0 +1,40 @@
+*** ../qt-mobility-src-1.0.0-beta1/configure	2010-02-12 03:54:12.000000000 -0500
+--- configure	2010-03-24 18:51:06.000000000 -0400
+***************
+*** 364,372 ****
+  }
+  
+  #compile tests
+! compileTest QMF qmf
+! compileTest NetworkManager networkmanager
+! compileTest "CoreWLAN (MacOS 10.6)" corewlan
+  
+  # Now module selection
+  # using 'expr match ....' should help a bit
+--- 364,372 ----
+  }
+  
+  #compile tests
+! #compileTest QMF qmf
+! #compileTest NetworkManager networkmanager
+! #compileTest "CoreWLAN (MacOS 10.6)" corewlan
+  
+  # Now module selection
+  # using 'expr match ....' should help a bit
+***************
+*** 436,442 ****
+  mkdir -p "$shadowpath/features"
+  
+  echo "Running qmake..."
+! if qmake -recursive "$relpath/qtmobility.pro"; then
+      echo ""
+      echo "configure has finished. You may run make or gmake to build the project now."
+  else
+--- 436,442 ----
+  mkdir -p "$shadowpath/features"
+  
+  echo "Running qmake..."
+! if qmake -spec macx-g++ -recursive "$relpath/qtmobility.pro"; then
+      echo ""
+      echo "configure has finished. You may run make or gmake to build the project now."
+  else

+ 4 - 1
Utilities/QtMobility/QtMobilityBeta1-patch.cmake.in

@@ -22,7 +22,10 @@ IF(UNIX)
     ${configured_patch_dir}/QtMobilityBeta1-unix.patch @ONLY)
   LIST(APPEND patch_files
     ${configured_patch_dir}/QtMobilityBeta1-unix.patch)
+  IF(APPLE)
+    LIST(APPEND patch_files ${patch_dir}/QtMobilityBeta1-apple.patch)
+  ENDIF()
 ENDIF()
 
 # Apply patches
-ctkMacroApplyPatches(@CTK_PATCH_EXECUTABLE@ @qtmobility_src_dir@ ${patch_files})
+ctkMacroApplyPatches(@CTK_PATCH_EXECUTABLE@ @qtmobility_src_dir@ "${patch_files}")

+ 15 - 1
Utilities/QtMobility/QtMobilityBeta1-win32.patch.in

@@ -1,3 +1,17 @@
+diff -crB ../qt-mobility-src-1.0.0-beta1/common.pri common.pri
+*** ./qt-mobility-src-1.0.0-beta1/common.pri	2010-02-12 03:54:11.000000000 -0500
+--- common.pri	2010-03-11 16:04:39.000000000 -0500
+***************
+*** 104,109 ****
+--- 104,111 ----
+      QMAKE_RPATHDIR += $$OUTPUT_DIR/lib
+  }
+  
++ contains(TEMPLATE,.*lib):DEFINES += QT_SHARED
++ 
+  maemo6 {
+      DEFINES+= Q_WS_MAEMO_6
+  }
 diff -crB ../qt-mobility-src-1.0.0-beta1/configure.bat configure.bat
 *** ./qt-mobility-src-1.0.0-beta1/configure.bat	2010-02-12 03:54:12.000000000 -0500
 --- configure.bat	2010-03-18 13:19:42.000000000 -0400
@@ -14,7 +28,7 @@ diff -crB ../qt-mobility-src-1.0.0-beta1/configure.bat configure.bat
   set MOBILITY_MODULES=bearer location contacts multimedia publishsubscribe versit messaging systeminfo serviceframework sensors
   set MOBILITY_MODULES_UNPARSED=
   set VC_TEMPLATE_OPTION=
-! set QT_PATH=@QT_BINARY_DIR@
+! set QT_PATH=@QT_BINARY_DIR@/
   set QMAKE_CACHE=%BUILD_PATH%\.qmake.cache
   
   if exist "%QMAKE_CACHE%" del %QMAKE_CACHE%