Explorar o código

Merge remote-tracking branch 'AndreasFetzer/issue-332-dicom-table-manager-integration-final'

Closes #332
Marco Nolden %!s(int64=12) %!d(string=hai) anos
pai
achega
f7938f4b63

+ 2 - 2
Applications/ctkDICOM/ctkDICOMMain.cpp

@@ -26,7 +26,7 @@
 #include <QResource>
 
 // CTK widget includes
-#include <ctkDICOMBrowser.h>
+#include <ctkDICOMAppWidget.h>
 
 // ctkDICOMCore includes
 #include "ctkDICOMDatabase.h"
@@ -79,7 +79,7 @@ int main(int argc, char** argv)
     }
   }
 
-  ctkDICOMBrowser DICOMApp;
+  ctkDICOMAppWidget DICOMApp;
 
   DICOMApp.setDatabaseDirectory(databaseDirectory);
   DICOMApp.show();

+ 8 - 0
Libs/DICOM/Widgets/CMakeLists.txt

@@ -31,6 +31,10 @@ set(KIT_SRCS
   ctkDICOMQueryWidget.h
   ctkDICOMServerNodeWidget.cpp
   ctkDICOMServerNodeWidget.h
+  ctkDICOMTableManager.h
+  ctkDICOMTableManager.cpp
+  ctkDICOMTableView.cpp
+  ctkDICOMTableView.h
   ctkDICOMThumbnailGenerator.cpp
   ctkDICOMThumbnailGenerator.h
   ctkDICOMThumbnailListWidget.cpp
@@ -48,6 +52,8 @@ set(KIT_MOC_SRCS
   ctkDICOMQueryRetrieveWidget.h
   ctkDICOMQueryWidget.h
   ctkDICOMServerNodeWidget.h
+  ctkDICOMTableManager.h
+  ctkDICOMTableView.h
   ctkDICOMThumbnailGenerator.h
   ctkDICOMThumbnailListWidget.h
   )
@@ -62,6 +68,8 @@ set(KIT_UI_FORMS
   Resources/UI/ctkDICOMQueryRetrieveWidget.ui
   Resources/UI/ctkDICOMQueryWidget.ui
   Resources/UI/ctkDICOMServerNodeWidget.ui
+  Resources/UI/ctkDICOMTableManager.ui
+  Resources/UI/ctkDICOMTableView.ui
 )
 
 # Resources

+ 7 - 0
Libs/DICOM/Widgets/Plugins/CMakeLists.txt

@@ -15,6 +15,11 @@ set(PLUGIN_SRCS
 
   ctkDICOMQueryRetrieveWidgetPlugin.cpp
   ctkDICOMQueryRetrieveWidgetPlugin.h
+
+  ctkDICOMTableManagerPlugin.cpp
+  ctkDICOMTableManagerPlugin.h
+  ctkDICOMTableViewPlugin.cpp
+  ctkDICOMTableViewPlugin.h
   )
 
 # Headers that should run through moc
@@ -22,6 +27,8 @@ set(PLUGIN_MOC_SRCS
   ctkDICOMWidgetsPlugins.h
 
   ctkDICOMQueryRetrieveWidgetPlugin.h
+  ctkDICOMTableManagerPlugin.h
+  ctkDICOMTableViewPlugin.h
   )
 
 # Resources

+ 68 - 0
Libs/DICOM/Widgets/Plugins/ctkDICOMTableManagerPlugin.cpp

@@ -0,0 +1,68 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// CTK includes
+#include "ctkDICOMTableManagerPlugin.h"
+#include "ctkDICOMTableManager.h"
+
+//-----------------------------------------------------------------------------
+ctkDICOMTableManagerPlugin::ctkDICOMTableManagerPlugin(QObject* pluginParent)
+  : QObject(pluginParent)
+{
+}
+
+//-----------------------------------------------------------------------------
+QWidget *ctkDICOMTableManagerPlugin::createWidget(QWidget *parentForWidget)
+{
+  ctkDICOMTableManager* newWidget = new ctkDICOMTableManager(parentForWidget);
+  return newWidget;
+}
+
+//-----------------------------------------------------------------------------
+QString ctkDICOMTableManagerPlugin::domXml() const
+{
+  return "<widget class=\"ctkDICOMTableManager\" \
+          name=\"DICOMTableManager\">\n"
+          "</widget>\n";
+}
+
+// --------------------------------------------------------------------------
+QIcon ctkDICOMTableManagerPlugin::icon() const
+{
+  return QIcon(":/Icons/listview.png");
+}
+
+//-----------------------------------------------------------------------------
+QString ctkDICOMTableManagerPlugin::includeFile() const
+{
+  return "ctkDICOMTableManager.h";
+}
+
+//-----------------------------------------------------------------------------
+bool ctkDICOMTableManagerPlugin::isContainer() const
+{
+  return false;
+}
+
+//-----------------------------------------------------------------------------
+QString ctkDICOMTableManagerPlugin::name() const
+{
+  return "ctkDICOMTableManager";
+}

+ 45 - 0
Libs/DICOM/Widgets/Plugins/ctkDICOMTableManagerPlugin.h

@@ -0,0 +1,45 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkDICOMTableManagerPlugin_h
+#define __ctkDICOMTableManagerPlugin_h
+
+// CTK include
+#include "ctkDICOMWidgetsAbstractPlugin.h"
+
+class CTK_DICOM_WIDGETS_PLUGINS_EXPORT ctkDICOMTableManagerPlugin
+  : public QObject
+  , public ctkDICOMWidgetsAbstractPlugin
+{
+  Q_OBJECT
+
+public:
+  ctkDICOMTableManagerPlugin(QObject *_parent = 0);
+
+  QWidget *createWidget(QWidget *_parent);
+  QString  domXml() const;
+  QIcon    icon() const;
+  QString  includeFile() const;
+  bool     isContainer() const;
+  QString  name() const;
+
+};
+
+#endif

+ 68 - 0
Libs/DICOM/Widgets/Plugins/ctkDICOMTableViewPlugin.cpp

@@ -0,0 +1,68 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// CTK includes
+#include "ctkDICOMTableViewPlugin.h"
+#include "ctkDICOMTableView.h"
+
+//-----------------------------------------------------------------------------
+ctkDICOMTableViewPlugin::ctkDICOMTableViewPlugin(QObject* pluginParent)
+  : QObject(pluginParent)
+{
+}
+
+//-----------------------------------------------------------------------------
+QWidget *ctkDICOMTableViewPlugin::createWidget(QWidget *parentForWidget)
+{
+  ctkDICOMTableView* newWidget = new ctkDICOMTableView(parentForWidget);
+  return newWidget;
+}
+
+//-----------------------------------------------------------------------------
+QString ctkDICOMTableViewPlugin::domXml() const
+{
+  return "<widget class=\"ctkDICOMTableView\" \
+          name=\"DICOMTableView\">\n"
+          "</widget>\n";
+}
+
+// --------------------------------------------------------------------------
+QIcon ctkDICOMTableViewPlugin::icon() const
+{
+  return QIcon(":/Icons/listview.png");
+}
+
+//-----------------------------------------------------------------------------
+QString ctkDICOMTableViewPlugin::includeFile() const
+{
+  return "ctkDICOMTableView.h";
+}
+
+//-----------------------------------------------------------------------------
+bool ctkDICOMTableViewPlugin::isContainer() const
+{
+  return false;
+}
+
+//-----------------------------------------------------------------------------
+QString ctkDICOMTableViewPlugin::name() const
+{
+  return "ctkDICOMTableView";
+}

+ 45 - 0
Libs/DICOM/Widgets/Plugins/ctkDICOMTableViewPlugin.h

@@ -0,0 +1,45 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkDICOMTableViewPlugin_h
+#define __ctkDICOMTableViewPlugin_h
+
+// CTK include
+#include "ctkDICOMWidgetsAbstractPlugin.h"
+
+class CTK_DICOM_WIDGETS_PLUGINS_EXPORT ctkDICOMTableViewPlugin
+  : public QObject
+  , public ctkDICOMWidgetsAbstractPlugin
+{
+  Q_OBJECT
+
+public:
+  ctkDICOMTableViewPlugin(QObject *_parent = 0);
+
+  QWidget *createWidget(QWidget *_parent);
+  QString  domXml() const;
+  QIcon    icon() const;
+  QString  includeFile() const;
+  bool     isContainer() const;
+  QString  name() const;
+
+};
+
+#endif

+ 4 - 0
Libs/DICOM/Widgets/Plugins/ctkDICOMWidgetsPlugins.h

@@ -27,6 +27,8 @@
 // CTK includes
 #include "ctkDICOMWidgetsPluginsExport.h"
 #include "ctkDICOMQueryRetrieveWidgetPlugin.h"
+#include "ctkDICOMTableManagerPlugin.h"
+#include "ctkDICOMTableViewPlugin.h"
 
 /// \class Group the plugins in one library
 class CTK_DICOM_WIDGETS_PLUGINS_EXPORT ctkDICOMWidgetsPlugins
@@ -41,6 +43,8 @@ public:
     {
     QList<QDesignerCustomWidgetInterface *> plugins;
     plugins << new ctkDICOMQueryRetrieveWidgetPlugin;
+    plugins << new ctkDICOMTableManagerPlugin;
+    plugins << new ctkDICOMTableViewPlugin;
     return plugins;
     }
 };

+ 4 - 497
Libs/DICOM/Widgets/Resources/UI/ctkDICOMBrowser.ui

@@ -96,25 +96,12 @@
          </property>
         </spacer>
        </item>
-       <item>
-        <widget class="QPushButton" name="SearchPopUpButton">
-         <property name="maximumSize">
-          <size>
-           <width>100</width>
-           <height>32</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Search</string>
-         </property>
-        </widget>
-       </item>
       </layout>
      </item>
     </layout>
    </item>
    <item>
-    <layout class="QHBoxLayout" name="QueryLayout" stretch="1,0">
+    <layout class="QHBoxLayout" name="QueryLayout" stretch="0">
      <property name="spacing">
       <number>0</number>
      </property>
@@ -125,347 +112,7 @@
       <number>12</number>
      </property>
      <item>
-      <widget class="QTreeView" name="TreeView">
-       <property name="alternatingRowColors">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item>
-      <layout class="QVBoxLayout" name="searchOptionLayout">
-       <property name="spacing">
-        <number>6</number>
-       </property>
-       <item>
-        <widget class="QDockWidget" name="SearchDockWidget">
-         <property name="floating">
-          <bool>false</bool>
-         </property>
-         <property name="features">
-          <set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
-         </property>
-         <property name="windowTitle">
-          <string/>
-         </property>
-         <widget class="QWidget" name="dockWidgetContents">
-          <layout class="QVBoxLayout" name="verticalLayout_4">
-           <item>
-            <widget class="ctkDICOMQueryWidget" name="SearchOption" native="true">
-             <property name="minimumSize">
-              <size>
-               <width>0</width>
-               <height>0</height>
-              </size>
-             </property>
-            </widget>
-           </item>
-          </layout>
-         </widget>
-        </widget>
-       </item>
-       <item>
-        <spacer name="VerticalSpacer">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>20</width>
-           <height>40</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-      </layout>
-     </item>
-    </layout>
-   </item>
-   <item>
-    <widget class="QFrame" name="UserFrame">
-     <property name="frameShape">
-      <enum>QFrame::StyledPanel</enum>
-     </property>
-     <property name="frameShadow">
-      <enum>QFrame::Raised</enum>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <layout class="QHBoxLayout" name="ViewerLayout">
-     <property name="leftMargin">
-      <number>12</number>
-     </property>
-     <property name="rightMargin">
-      <number>12</number>
-     </property>
-     <property name="bottomMargin">
-      <number>0</number>
-     </property>
-     <item>
-      <layout class="QVBoxLayout" name="verticalLayout">
-       <item>
-        <widget class="ctkDICOMThumbnailListWidget" name="ThumbnailsWidget" native="true">
-         <property name="sizePolicy">
-          <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-           <horstretch>0</horstretch>
-           <verstretch>0</verstretch>
-          </sizepolicy>
-         </property>
-         <property name="minimumSize">
-          <size>
-           <width>0</width>
-           <height>200</height>
-          </size>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <layout class="QHBoxLayout" name="horizontalLayout_3">
-         <item>
-          <spacer name="horizontalSpacer">
-           <property name="orientation">
-            <enum>Qt::Horizontal</enum>
-           </property>
-           <property name="sizeHint" stdset="0">
-            <size>
-             <width>40</width>
-             <height>20</height>
-            </size>
-           </property>
-          </spacer>
-         </item>
-         <item>
-          <widget class="QSlider" name="ThumbnailWidthSlider">
-           <property name="maximumSize">
-            <size>
-             <width>200</width>
-             <height>16777215</height>
-            </size>
-           </property>
-           <property name="minimum">
-            <number>64</number>
-           </property>
-           <property name="maximum">
-            <number>256</number>
-           </property>
-           <property name="value">
-            <number>64</number>
-           </property>
-           <property name="orientation">
-            <enum>Qt::Horizontal</enum>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <spacer name="horizontalSpacer_2">
-           <property name="orientation">
-            <enum>Qt::Horizontal</enum>
-           </property>
-           <property name="sizeHint" stdset="0">
-            <size>
-             <width>40</width>
-             <height>20</height>
-            </size>
-           </property>
-          </spacer>
-         </item>
-        </layout>
-       </item>
-      </layout>
-     </item>
-     <item>
-      <widget class="QFrame" name="PreviewFrame">
-       <property name="minimumSize">
-        <size>
-         <width>256</width>
-         <height>256</height>
-        </size>
-       </property>
-       <layout class="QVBoxLayout" name="verticalLayout_3">
-        <item>
-         <layout class="QHBoxLayout" name="HorizontalLayout">
-          <item>
-           <widget class="QPushButton" name="PrevStudyButton">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-              <horstretch>32</horstretch>
-              <verstretch>32</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="maximumSize">
-             <size>
-              <width>40</width>
-              <height>32</height>
-             </size>
-            </property>
-            <property name="toolTip">
-             <string>Previous study</string>
-            </property>
-            <property name="text">
-             <string>&lt;&lt;&lt;</string>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <widget class="QPushButton" name="PrevSeriesButton">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-              <horstretch>32</horstretch>
-              <verstretch>32</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="maximumSize">
-             <size>
-              <width>40</width>
-              <height>32</height>
-             </size>
-            </property>
-            <property name="toolTip">
-             <string>Previous series</string>
-            </property>
-            <property name="text">
-             <string>&lt;&lt;</string>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <widget class="QPushButton" name="PrevImageButton">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-              <horstretch>32</horstretch>
-              <verstretch>32</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="maximumSize">
-             <size>
-              <width>40</width>
-              <height>32</height>
-             </size>
-            </property>
-            <property name="toolTip">
-             <string>Previous image</string>
-            </property>
-            <property name="text">
-             <string>&lt;</string>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <widget class="QPushButton" name="NextImageButton">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-              <horstretch>32</horstretch>
-              <verstretch>32</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="maximumSize">
-             <size>
-              <width>40</width>
-              <height>32</height>
-             </size>
-            </property>
-            <property name="toolTip">
-             <string>Next image</string>
-            </property>
-            <property name="text">
-             <string>&gt;</string>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <widget class="QPushButton" name="NextSeriesButton">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-              <horstretch>32</horstretch>
-              <verstretch>32</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="maximumSize">
-             <size>
-              <width>40</width>
-              <height>32</height>
-             </size>
-            </property>
-            <property name="toolTip">
-             <string>Next series</string>
-            </property>
-            <property name="text">
-             <string>&gt;&gt;</string>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <widget class="QPushButton" name="NextStudyButton">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-              <horstretch>32</horstretch>
-              <verstretch>32</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="maximumSize">
-             <size>
-              <width>40</width>
-              <height>32</height>
-             </size>
-            </property>
-            <property name="toolTip">
-             <string>Next study</string>
-            </property>
-            <property name="text">
-             <string>&gt;&gt;&gt;</string>
-            </property>
-           </widget>
-          </item>
-         </layout>
-        </item>
-        <item>
-         <widget class="ctkDICOMItemView" name="ImagePreview" native="true">
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <layout class="QHBoxLayout" name="horizontalLayout_2">
-          <item>
-           <widget class="QCheckBox" name="AutoPlayCheckbox">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
-              <horstretch>0</horstretch>
-              <verstretch>0</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="maximumSize">
-             <size>
-              <width>90</width>
-              <height>16777215</height>
-             </size>
-            </property>
-            <property name="text">
-             <string>auto-play</string>
-            </property>
-           </widget>
-          </item>
-          <item>
-           <widget class="QSlider" name="PlaySlider">
-            <property name="sizePolicy">
-             <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
-              <horstretch>0</horstretch>
-              <verstretch>0</verstretch>
-             </sizepolicy>
-            </property>
-            <property name="orientation">
-             <enum>Qt::Horizontal</enum>
-            </property>
-           </widget>
-          </item>
-         </layout>
-        </item>
-       </layout>
-      </widget>
+      <widget class="ctkDICOMTableManager" name="dicomTableManager" native="true"/>
      </item>
     </layout>
    </item>
@@ -522,12 +169,6 @@
  </widget>
  <customwidgets>
   <customwidget>
-   <class>ctkDICOMQueryWidget</class>
-   <extends>QWidget</extends>
-   <header>ctkDICOMQueryWidget.h</header>
-   <container>1</container>
-  </customwidget>
-  <customwidget>
    <class>ctkDirectoryButton</class>
    <extends>QWidget</extends>
    <header>ctkDirectoryButton.h</header>
@@ -537,15 +178,9 @@
    </slots>
   </customwidget>
   <customwidget>
-   <class>ctkDICOMThumbnailListWidget</class>
-   <extends>QWidget</extends>
-   <header>ctkDICOMThumbnailListWidget.h</header>
-   <container>1</container>
-  </customwidget>
-  <customwidget>
-   <class>ctkDICOMItemView</class>
+   <class>ctkDICOMTableManager</class>
    <extends>QWidget</extends>
-   <header location="global">ctkDICOMItemView.h</header>
+   <header>ctkDICOMTableManager.h</header>
    <container>1</container>
   </customwidget>
  </customwidgets>
@@ -600,134 +235,6 @@
    </hints>
   </connection>
   <connection>
-   <sender>NextImageButton</sender>
-   <signal>clicked()</signal>
-   <receiver>ctkDICOMBrowser</receiver>
-   <slot>onNextImage()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>608</x>
-     <y>395</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>369</x>
-     <y>318</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>PrevImageButton</sender>
-   <signal>clicked()</signal>
-   <receiver>ctkDICOMBrowser</receiver>
-   <slot>onPreviousImage()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>566</x>
-     <y>395</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>369</x>
-     <y>318</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>NextSeriesButton</sender>
-   <signal>clicked()</signal>
-   <receiver>ctkDICOMBrowser</receiver>
-   <slot>onNextSeries()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>650</x>
-     <y>395</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>369</x>
-     <y>318</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>PrevSeriesButton</sender>
-   <signal>clicked()</signal>
-   <receiver>ctkDICOMBrowser</receiver>
-   <slot>onPreviousSeries()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>524</x>
-     <y>395</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>369</x>
-     <y>318</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>NextStudyButton</sender>
-   <signal>clicked()</signal>
-   <receiver>ctkDICOMBrowser</receiver>
-   <slot>onNextStudy()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>692</x>
-     <y>395</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>369</x>
-     <y>318</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>PrevStudyButton</sender>
-   <signal>clicked()</signal>
-   <receiver>ctkDICOMBrowser</receiver>
-   <slot>onPreviousStudy()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>482</x>
-     <y>395</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>369</x>
-     <y>318</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>AutoPlayCheckbox</sender>
-   <signal>stateChanged(int)</signal>
-   <receiver>ctkDICOMBrowser</receiver>
-   <slot>onAutoPlayCheckboxStateChanged(int)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>430</x>
-     <y>596</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>369</x>
-     <y>318</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>ThumbnailWidthSlider</sender>
-   <signal>valueChanged(int)</signal>
-   <receiver>ctkDICOMBrowser</receiver>
-   <slot>onThumbnailWidthSliderValueChanged(int)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>236</x>
-     <y>610</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>369</x>
-     <y>318</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
    <sender>ActionRemove</sender>
    <signal>triggered()</signal>
    <receiver>ctkDICOMBrowser</receiver>

+ 192 - 0
Libs/DICOM/Widgets/Resources/UI/ctkDICOMTableManager.ui

@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ctkDICOMTableManager</class>
+ <widget class="QWidget" name="ctkDICOMTableManager">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>833</width>
+    <height>455</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QSplitter" name="tableSplitter">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="styleSheet">
+      <string notr="true">QSplitter::handle {background-color: rgb(200,200,200);}
+QSplitter::handle:horizontal {width: 2px;}
+QSplitter::handle:vertical {height: 2px;}</string>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <widget class="ctkDICOMTableView" name="patientsTable"/>
+     <widget class="ctkDICOMTableView" name="studiesTable"/>
+     <widget class="ctkDICOMTableView" name="seriesTable"/>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>ctkDICOMTableView</class>
+   <extends>QWidget</extends>
+   <header>ctkDICOMTableView.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+   <sender>patientsTable</sender>
+   <signal>selectionChanged(QStringList)</signal>
+   <receiver>ctkDICOMTableManager</receiver>
+   <slot>onPatientsSelectionChanged(QStringList)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>220</x>
+     <y>186</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>343</x>
+     <y>166</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>studiesTable</sender>
+   <signal>selectionChanged(QStringList)</signal>
+   <receiver>ctkDICOMTableManager</receiver>
+   <slot>onStudiesSelectionChanged(QStringList)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>220</x>
+     <y>186</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>343</x>
+     <y>166</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>patientsTable</sender>
+   <signal>queryChanged(QStringList)</signal>
+   <receiver>ctkDICOMTableManager</receiver>
+   <slot>onPatientsQueryChanged(QStringList)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>220</x>
+     <y>186</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>343</x>
+     <y>166</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>studiesTable</sender>
+   <signal>queryChanged(QStringList)</signal>
+   <receiver>ctkDICOMTableManager</receiver>
+   <slot>onStudiesQueryChanged(QStringList)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>220</x>
+     <y>186</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>343</x>
+     <y>166</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>patientsTable</sender>
+   <signal>queryChanged(QStringList)</signal>
+   <receiver>studiesTable</receiver>
+   <slot>onUpdateQuery(QStringList)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>220</x>
+     <y>186</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>343</x>
+     <y>166</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>studiesTable</sender>
+   <signal>queryChanged(QStringList)</signal>
+   <receiver>seriesTable</receiver>
+   <slot>onUpdateQuery(QStringList)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>550</x>
+     <y>140</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>671</x>
+     <y>133</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>patientsTable</sender>
+   <signal>selectionChanged(QStringList)</signal>
+   <receiver>studiesTable</receiver>
+   <slot>onUpdateQuery(QStringList)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>206</x>
+     <y>244</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>383</x>
+     <y>267</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>studiesTable</sender>
+   <signal>selectionChanged(QStringList)</signal>
+   <receiver>seriesTable</receiver>
+   <slot>onUpdateQuery(QStringList)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>502</x>
+     <y>234</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>608</x>
+     <y>234</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+ <slots>
+  <signal>seriesSelectionChanged(QStringList)</signal>
+  <signal>seriesSelectionChanged(QItemSelection,QItemSelection)</signal>
+  <signal>studiesSelectionChanged(QStringList)</signal>
+  <signal>studiesSelectionsChanged(QItemSelection,QItemSelection)</signal>
+  <signal>patientsSelectionChanged(QStringList)</signal>
+  <signal>patientsSelectionChanged(QItemSelection,QItemSelection)</signal>
+ </slots>
+</ui>

+ 105 - 0
Libs/DICOM/Widgets/Resources/UI/ctkDICOMTableView.ui

@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ctkDICOMTableView</class>
+ <widget class="QWidget" name="ctkDICOMTableView">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>794</width>
+    <height>462</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="lblTableName">
+       <property name="text">
+        <string>TextLabel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="ctkSearchBox" name="leSearchBox">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="styleSheet">
+        <string notr="true"/>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="placeholderText">
+        <string/>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QTableView" name="tblDicomDatabaseView">
+     <property name="styleSheet">
+      <string notr="true">font: 75 10pt &quot;Lucida Grande&quot;;</string>
+     </property>
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="selectionBehavior">
+      <enum>QAbstractItemView::SelectRows</enum>
+     </property>
+     <property name="textElideMode">
+      <enum>Qt::ElideMiddle</enum>
+     </property>
+     <property name="verticalScrollMode">
+      <enum>QAbstractItemView::ScrollPerPixel</enum>
+     </property>
+     <property name="horizontalScrollMode">
+      <enum>QAbstractItemView::ScrollPerPixel</enum>
+     </property>
+     <property name="showGrid">
+      <bool>false</bool>
+     </property>
+     <property name="gridStyle">
+      <enum>Qt::SolidLine</enum>
+     </property>
+     <attribute name="horizontalHeaderStretchLastSection">
+      <bool>true</bool>
+     </attribute>
+     <attribute name="verticalHeaderVisible">
+      <bool>false</bool>
+     </attribute>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>ctkSearchBox</class>
+   <extends>QLineEdit</extends>
+   <header location="global">ctkSearchBox.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>

+ 26 - 527
Libs/DICOM/Widgets/ctkDICOMBrowser.cpp

@@ -21,23 +21,14 @@
 // std includes
 #include <iostream>
 
-#include <dcmimage.h>
-
 // Qt includes
 #include <QAction>
 #include <QCoreApplication>
 #include <QCheckBox>
 #include <QDebug>
 #include <QMessageBox>
-#include <QMetaType>
-#include <QModelIndex>
-#include <QPersistentModelIndex>
 #include <QProgressDialog>
 #include <QSettings>
-#include <QSlider>
-#include <QTabBar>
-#include <QTimer>
-#include <QTreeView>
 
 // ctkWidgets includes
 #include "ctkDirectoryButton.h"
@@ -45,14 +36,10 @@
 
 // ctkDICOMCore includes
 #include "ctkDICOMDatabase.h"
-#include "ctkDICOMFilterProxyModel.h"
 #include "ctkDICOMIndexer.h"
-#include "ctkDICOMModel.h"
 
 // ctkDICOMWidgets includes
 #include "ctkDICOMBrowser.h"
-#include "ctkDICOMThumbnailGenerator.h"
-#include "ctkThumbnailLabel.h"
 #include "ctkDICOMQueryResultsTabWidget.h"
 #include "ctkDICOMQueryRetrieveWidget.h"
 #include "ctkDICOMQueryWidget.h"
@@ -63,8 +50,6 @@
 #include <ctkLogger.h>
 static ctkLogger logger("org.commontk.DICOM.Widgets.ctkDICOMBrowser");
 
-Q_DECLARE_METATYPE(QPersistentModelIndex);
-
 //----------------------------------------------------------------------------
 class ctkDICOMBrowserPrivate: public Ui_ctkDICOMBrowser
 {
@@ -79,9 +64,6 @@ public:
   ctkDICOMQueryRetrieveWidget* QueryRetrieveWidget;
 
   QSharedPointer<ctkDICOMDatabase> DICOMDatabase;
-  QSharedPointer<ctkDICOMThumbnailGenerator> ThumbnailGenerator;
-  ctkDICOMModel DICOMModel;
-  ctkDICOMFilterProxyModel DICOMProxyModel;
   QSharedPointer<ctkDICOMIndexer> DICOMIndexer;
   QProgressDialog *IndexerProgress;
   QProgressDialog *UpdateSchemaProgress;
@@ -92,10 +74,6 @@ public:
   // used when suspending the ctkDICOMModel
   QSqlDatabase EmptyDatabase;
 
-  QTimer* AutoPlayTimer;
-
-  bool IsSearchWidgetPopUpMode;
-
   // local count variables to keep track of the number of items
   // added to the database during an import operation
   bool DisplayImportSummary;
@@ -110,8 +88,6 @@ public:
 
 ctkDICOMBrowserPrivate::ctkDICOMBrowserPrivate(ctkDICOMBrowser* parent): q_ptr(parent){
   DICOMDatabase = QSharedPointer<ctkDICOMDatabase> (new ctkDICOMDatabase);
-  ThumbnailGenerator = QSharedPointer <ctkDICOMThumbnailGenerator> (new ctkDICOMThumbnailGenerator);
-  DICOMDatabase->setThumbnailGenerator(ThumbnailGenerator.data());
   DICOMIndexer = QSharedPointer<ctkDICOMIndexer> (new ctkDICOMIndexer);
   IndexerProgress = 0;
   UpdateSchemaProgress = 0;
@@ -154,9 +130,6 @@ void ctkDICOMBrowserPrivate::showUpdateSchemaDialog()
     UpdateSchemaProgress->setMinimumDuration(0);
     UpdateSchemaProgress->setValue(0);
 
-    //q->connect(UpdateSchemaProgress, SIGNAL(canceled()), 
-     //       DICOMIndexer.data(), SLOT(cancel()));
-
     q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdateStarted(int)),
             UpdateSchemaProgress, SLOT(setMaximum(int)));
     q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdateProgress(int)),
@@ -167,12 +140,6 @@ void ctkDICOMBrowserPrivate::showUpdateSchemaDialog()
     // close the dialog
     q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdated()),
             UpdateSchemaProgress, SLOT(close()));
-    // reset the database to show new data
-    q->connect(DICOMDatabase.data(), SIGNAL(schemaUpdated()),
-            &DICOMModel, SLOT(reset()));
-    // reset the database if canceled
-    q->connect(UpdateSchemaProgress, SIGNAL(canceled()), 
-            &DICOMModel, SLOT(reset()));
     }
   UpdateSchemaProgress->show();
 }
@@ -209,14 +176,9 @@ void ctkDICOMBrowserPrivate::showIndexerDialog()
     // close the dialog
     q->connect(DICOMIndexer.data(), SIGNAL(indexingComplete()),
             IndexerProgress, SLOT(close()));
-    // reset the database to show new data
-    q->connect(DICOMIndexer.data(), SIGNAL(indexingComplete()),
-            &DICOMModel, SLOT(reset()));
     // stop indexing and reset the database if canceled
     q->connect(IndexerProgress, SIGNAL(canceled()), 
             DICOMIndexer.data(), SLOT(cancel()));
-    q->connect(IndexerProgress, SIGNAL(canceled()), 
-            &DICOMModel, SLOT(reset()));
 
     // allow users of this widget to know that the process has finished
     q->connect(IndexerProgress, SIGNAL(canceled()), 
@@ -238,25 +200,6 @@ ctkDICOMBrowser::ctkDICOMBrowser(QWidget* _parent):Superclass(_parent),
 
   d->setupUi(this);
 
-  this->setSearchWidgetPopUpMode(false);
-
-  //Hide image previewer buttons
-  d->NextImageButton->hide();
-  d->PrevImageButton->hide();
-  d->NextSeriesButton->hide();
-  d->PrevSeriesButton->hide();
-  d->NextStudyButton->hide();
-  d->PrevStudyButton->hide();
-
-  //Enable sorting in tree view
-  d->TreeView->setSortingEnabled(true);
-  d->TreeView->setSelectionBehavior(QAbstractItemView::SelectRows);
-  d->DICOMProxyModel.setSourceModel(&d->DICOMModel);
-  d->TreeView->setModel(&d->DICOMModel);
-
-  d->ThumbnailsWidget->setThumbnailSize(
-    QSize(d->ThumbnailWidthSlider->value(), d->ThumbnailWidthSlider->value()));
-
   // signals related to tracking inserts
   connect(d->DICOMDatabase.data(), SIGNAL(patientAdded(int,QString,QString,QString)), this,
                               SLOT(onPatientAdded(int,QString,QString,QString)));
@@ -264,9 +207,6 @@ ctkDICOMBrowser::ctkDICOMBrowser(QWidget* _parent):Superclass(_parent),
   connect(d->DICOMDatabase.data(), SIGNAL(seriesAdded(QString)), this, SLOT(onSeriesAdded(QString)));
   connect(d->DICOMDatabase.data(), SIGNAL(instanceAdded(QString)), this, SLOT(onInstanceAdded(QString)));
 
-  // Treeview signals
-  connect(d->TreeView, SIGNAL(collapsed(QModelIndex)), this, SLOT(onTreeCollapsed(QModelIndex)));
-  connect(d->TreeView, SIGNAL(expanded(QModelIndex)), this, SLOT(onTreeExpanded(QModelIndex)));
 
   //Set ToolBar button style
   d->ToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
@@ -287,6 +227,15 @@ ctkDICOMBrowser::ctkDICOMBrowser(QWidget* _parent):Superclass(_parent),
   this->setDatabaseDirectory(databaseDirectory);
   d->DirectoryButton->setDirectory(databaseDirectory);
 
+  // TableView signals
+  connect(d->dicomTableManager, SIGNAL(patientsSelectionChanged(const QItemSelection&, const QItemSelection&)),
+          this, SLOT(onModelSelected(const QItemSelection&,const QItemSelection&)));
+  connect(d->dicomTableManager, SIGNAL(studiesSelectionChanged(const QItemSelection&, const QItemSelection&)),
+          this, SLOT(onModelSelected(const QItemSelection&,const QItemSelection&)));
+  connect(d->dicomTableManager, SIGNAL(seriesSelectionChanged(const QItemSelection&, const QItemSelection&)),
+          this, SLOT(onModelSelected(const QItemSelection&,const QItemSelection&)));
+  d->dicomTableManager->setCTKDICOMDatabase(d->DICOMDatabase.data());
+
   connect(d->DirectoryButton, SIGNAL(directoryChanged(QString)), this, SLOT(setDatabaseDirectory(QString)));
 
   //Initialize import widget
@@ -299,24 +248,10 @@ ctkDICOMBrowser::ctkDICOMBrowser(QWidget* _parent):Superclass(_parent),
   d->ImportDialog->setWindowModality(Qt::ApplicationModal);
 
   //connect signal and slots
-  connect(d->TreeView, SIGNAL(clicked(QModelIndex)), d->ThumbnailsWidget, SLOT(addThumbnails(QModelIndex)));
-  connect(d->TreeView, SIGNAL(clicked(QModelIndex)), d->ImagePreview, SLOT(onModelSelected(QModelIndex)));
-  connect(d->TreeView, SIGNAL(clicked(QModelIndex)), this, SLOT(onModelSelected(QModelIndex)));
-
-  connect(d->ThumbnailsWidget, SIGNAL(selected(ctkThumbnailLabel)), this, SLOT(onThumbnailSelected(ctkThumbnailLabel)));
-  connect(d->ThumbnailsWidget, SIGNAL(doubleClicked(ctkThumbnailLabel)), this, SLOT(onThumbnailDoubleClicked(ctkThumbnailLabel)));
   connect(d->ImportDialog, SIGNAL(fileSelected(QString)),this,SLOT(onImportDirectory(QString)));
 
   connect(d->QueryRetrieveWidget, SIGNAL(canceled()), d->QueryRetrieveWidget, SLOT(hide()) );
   connect(d->QueryRetrieveWidget, SIGNAL(canceled()), this, SLOT(onQueryRetrieveFinished()) );
-
-  connect(d->ImagePreview, SIGNAL(requestNextImage()), this, SLOT(onNextImage()));
-  connect(d->ImagePreview, SIGNAL(requestPreviousImage()), this, SLOT(onPreviousImage()));
-  connect(d->ImagePreview, SIGNAL(imageDisplayed(int,int)), this, SLOT(onImagePreviewDisplayed(int,int)));
-
-  connect(d->SearchOption, SIGNAL(parameterChanged()), this, SLOT(onSearchParameterChanged()));
-
-  connect(d->PlaySlider, SIGNAL(valueChanged(int)), d->ImagePreview, SLOT(displayImage(int)));
 }
 
 //----------------------------------------------------------------------------
@@ -414,18 +349,11 @@ void ctkDICOMBrowser::setDatabaseDirectory(const QString& directory)
   // update the database schema if needed and provide progress
   this->updateDatabaseSchemaIfNeeded();
 
-  d->DICOMModel.setDatabase(d->DICOMDatabase->database());
-  d->DICOMModel.setEndLevel(ctkDICOMModel::SeriesType);
-  d->TreeView->resizeColumnToContents(0);
-
   //pass DICOM database instance to Import widget
-  // d->ImportDialog->setDICOMDatabase(d->DICOMDatabase);
   d->QueryRetrieveWidget->setRetrieveDatabase(d->DICOMDatabase);
 
   // update the button and let any connected slots know about the change
   d->DirectoryButton->setDirectory(directory);
-  d->ThumbnailsWidget->setDatabaseDirectory(directory);
-  d->ImagePreview->setDatabaseDirectory(directory);
   emit databaseDirectoryChanged(directory);
 }
 
@@ -436,12 +364,6 @@ QString ctkDICOMBrowser::databaseDirectory() const
   return settings.value("DatabaseDirectory").toString();
 }
 
-//----------------------------------------------------------------------------
-bool ctkDICOMBrowser::searchWidgetPopUpMode(){
-  Q_D(ctkDICOMBrowser);
-  return d->IsSearchWidgetPopUpMode;
-}
-
 //------------------------------------------------------------------------------
 void ctkDICOMBrowser::setTagsToPrecache( const QStringList tags)
 {
@@ -465,29 +387,10 @@ ctkDICOMDatabase* ctkDICOMBrowser::database(){
 }
 
 //----------------------------------------------------------------------------
-void ctkDICOMBrowser::setSearchWidgetPopUpMode(bool flag){
+ctkDICOMTableManager* ctkDICOMBrowser::dicomTableManager()
+{
   Q_D(ctkDICOMBrowser);
-
-  if(flag)
-    {
-    d->SearchDockWidget->setTitleBarWidget(0);
-    d->SearchPopUpButton->show();
-    d->SearchDockWidget->hide();
-    d->SearchDockWidget->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
-    connect(d->SearchDockWidget, SIGNAL(topLevelChanged(bool)), this, SLOT(onSearchWidgetTopLevelChanged(bool)));
-    connect(d->SearchPopUpButton, SIGNAL(clicked()), this, SLOT(onSearchPopUpButtonClicked()));
-    }
-  else
-    {
-    d->SearchDockWidget->setTitleBarWidget(new QWidget());
-    d->SearchPopUpButton->hide();
-    d->SearchDockWidget->show();
-    d->SearchDockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
-    disconnect(d->SearchDockWidget, SIGNAL(topLevelChanged(bool)), this, SLOT(onSearchWidgetTopLevelChanged(bool)));
-    disconnect(d->SearchPopUpButton, SIGNAL(clicked()), this, SLOT(onSearchPopUpButtonClicked()));
-    }
-
-  d->IsSearchWidgetPopUpMode = flag;
+  return d->dicomTableManager;
 }
 
 //----------------------------------------------------------------------------
@@ -529,7 +432,6 @@ void ctkDICOMBrowser::openQueryDialog()
 void ctkDICOMBrowser::onQueryRetrieveFinished()
 {
   Q_D(ctkDICOMBrowser);
-  d->DICOMModel.reset();
   emit this->queryRetrieveFinished();
 }
 
@@ -537,93 +439,22 @@ void ctkDICOMBrowser::onQueryRetrieveFinished()
 void ctkDICOMBrowser::onRemoveAction()
 {
   Q_D(ctkDICOMBrowser);
+  QStringList selectedSeriesUIDs = d->dicomTableManager->currentSeriesSelection();
 
-  //d->QueryRetrieveWidget->show();
-  // d->QueryRetrieveWidget->raise();
-  std::cout << "on remove" << std::endl;
-  QModelIndexList selection = d->TreeView->selectionModel()->selectedIndexes();
-  std::cout << selection.size() << std::endl;
-  QModelIndex index;
-  foreach(index,selection)
-  {
-    QModelIndex index0 = index.sibling(index.row(), 0);
-    if ( d->DICOMModel.data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::SeriesType))
+  foreach (const QString& uid, selectedSeriesUIDs)
     {
-      QString seriesUID = d->DICOMModel.data(index0,ctkDICOMModel::UIDRole).toString();
-      d->DICOMDatabase->removeSeries(seriesUID);
+      d->DICOMDatabase->removeSeries(uid);
     }
-    else if ( d->DICOMModel.data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::StudyType))
+  QStringList selectedStudiesUIDs = d->dicomTableManager->currentStudiesSelection();
+  foreach (const QString& uid, selectedStudiesUIDs)
     {
-      QString studyUID = d->DICOMModel.data(index0,ctkDICOMModel::UIDRole).toString();
-      d->DICOMDatabase->removeStudy(studyUID);
+      d->DICOMDatabase->removeStudy(uid);
     }
-    else if ( d->DICOMModel.data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::PatientType))
+  QStringList selectedPatientUIDs = d->dicomTableManager->currentPatientsSelection();
+  foreach (const QString& uid, selectedPatientUIDs)
     {
-      QString patientUID = d->DICOMModel.data(index0,ctkDICOMModel::UIDRole).toString();
-      d->DICOMDatabase->removePatient(patientUID);
+      d->DICOMDatabase->removePatient(uid);
     }
-  }
-  d->DICOMModel.reset();
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::suspendModel()
-{
-  Q_D(ctkDICOMBrowser);
-
-  d->DICOMModel.setDatabase(d->EmptyDatabase);
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::resumeModel()
-{
-  Q_D(ctkDICOMBrowser);
-
-  d->DICOMModel.setDatabase(d->DICOMDatabase->database());
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::resetModel()
-{
-  Q_D(ctkDICOMBrowser);
-
-  d->DICOMModel.reset();
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onThumbnailSelected(const ctkThumbnailLabel& widget)
-{
-    Q_D(ctkDICOMBrowser);
-
-  QModelIndex index = widget.property("sourceIndex").value<QPersistentModelIndex>();
-  if(index.isValid())
-    {
-    d->ImagePreview->onModelSelected(index);
-    }
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onThumbnailDoubleClicked(const ctkThumbnailLabel& widget)
-{
-    Q_D(ctkDICOMBrowser);
-
-    QModelIndex index = widget.property("sourceIndex").value<QPersistentModelIndex>();
-
-    if(!index.isValid())
-      {
-      return;
-      }
-
-    ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
-    QModelIndex index0 = index.sibling(index.row(), 0);
-
-    if(model && (model->data(index0,ctkDICOMModel::TypeRole) != static_cast<int>(ctkDICOMModel::ImageType)))
-      {
-        this->onModelSelected(index0);
-        d->TreeView->setCurrentIndex(index0);
-        d->ThumbnailsWidget->addThumbnails(index0);
-        d->ImagePreview->onModelSelected(index0);
-      }
 }
 
 //----------------------------------------------------------------------------
@@ -698,342 +529,10 @@ void ctkDICOMBrowser::onImportDirectory(QString directory)
 }
 
 //----------------------------------------------------------------------------
-void ctkDICOMBrowser::onModelSelected(const QModelIndex &index){
-Q_D(ctkDICOMBrowser);
-
-    ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(index.model()));
-
-    if(model)
-      {
-        QModelIndex index0 = index.sibling(index.row(), 0);
-
-        if ( model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::PatientType) )
-          {
-          d->NextImageButton->show();
-          d->PrevImageButton->show();
-          d->NextSeriesButton->show();
-          d->PrevSeriesButton->show();
-          d->NextStudyButton->show();
-          d->PrevStudyButton->show();
-          }
-        else if ( model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::StudyType) )
-          {
-          d->NextImageButton->show();
-          d->PrevImageButton->show();
-          d->NextSeriesButton->show();
-          d->PrevSeriesButton->show();
-          d->NextStudyButton->hide();
-          d->PrevStudyButton->hide();
-          }
-        else if ( model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::SeriesType) )
-          {
-          d->NextImageButton->show();
-          d->PrevImageButton->show();
-          d->NextSeriesButton->hide();
-          d->PrevSeriesButton->hide();
-          d->NextStudyButton->hide();
-          d->PrevStudyButton->hide();
-          }
-        else
-          {
-          d->NextImageButton->hide();
-          d->PrevImageButton->hide();
-          d->NextSeriesButton->hide();
-          d->PrevSeriesButton->hide();
-          d->NextStudyButton->hide();
-          d->PrevStudyButton->hide();
-          }
-        d->ActionRemove->setEnabled(
-            model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::SeriesType) ||
-            model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::StudyType) ||
-            model->data(index0,ctkDICOMModel::TypeRole) == static_cast<int>(ctkDICOMModel::PatientType) );
-        }
-
-      else
-        {
-        d->NextImageButton->hide();
-        d->PrevImageButton->hide();
-        d->NextSeriesButton->hide();
-        d->PrevSeriesButton->hide();
-        d->NextStudyButton->hide();
-        d->PrevStudyButton->hide();
-        d->ActionRemove->setEnabled(false);
-        }
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onNextImage(){
-    Q_D(ctkDICOMBrowser);
-
-    QModelIndex currentIndex = d->ImagePreview->currentImageIndex();
-
-    if(currentIndex.isValid())
-      {
-      ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
-
-      if(model)
-        {
-        QModelIndex seriesIndex = currentIndex.parent();
-
-        int imageCount = model->rowCount(seriesIndex);
-        int imageID = currentIndex.row();
-
-        imageID = (imageID+1)%imageCount;
-
-        int max = d->PlaySlider->maximum();
-        if(imageID > 0 && imageID < max)
-          {
-            d->PlaySlider->setValue(imageID);
-          }
-
-        QModelIndex nextIndex = currentIndex.sibling(imageID, 0);
-
-        d->ImagePreview->onModelSelected(nextIndex);
-        d->ThumbnailsWidget->selectThumbnailFromIndex(nextIndex);
-        }
-      }
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onPreviousImage(){
-    Q_D(ctkDICOMBrowser);
-
-    QModelIndex currentIndex = d->ImagePreview->currentImageIndex();
-
-    if(currentIndex.isValid())
-      {
-      ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
-
-      if(model)
-        {
-        QModelIndex seriesIndex = currentIndex.parent();
-
-        int imageCount = model->rowCount(seriesIndex);
-        int imageID = currentIndex.row();
-
-        imageID--;
-        if(imageID < 0) imageID += imageCount;
-
-        int max = d->PlaySlider->maximum();
-        if(imageID > 0 && imageID < max)
-          {
-            d->PlaySlider->setValue(imageID);
-          }
-
-        QModelIndex prevIndex = currentIndex.sibling(imageID, 0);
-
-        d->ImagePreview->onModelSelected(prevIndex);
-        d->ThumbnailsWidget->selectThumbnailFromIndex(prevIndex);
-        }
-      }
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onNextSeries(){
-    Q_D(ctkDICOMBrowser);
-
-    QModelIndex currentIndex = d->ImagePreview->currentImageIndex();
-
-    if(currentIndex.isValid())
-      {
-      ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
-
-      if(model)
-        {
-        QModelIndex seriesIndex = currentIndex.parent();
-        QModelIndex studyIndex = seriesIndex.parent();
-
-        int seriesCount = model->rowCount(studyIndex);
-        int seriesID = seriesIndex.row();
-
-        seriesID = (seriesID + 1)%seriesCount;
-
-        QModelIndex nextIndex = seriesIndex.sibling(seriesID, 0);
-
-        d->ImagePreview->onModelSelected(nextIndex);
-        d->ThumbnailsWidget->selectThumbnailFromIndex(nextIndex);
-        }
-      }
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onPreviousSeries(){
-    Q_D(ctkDICOMBrowser);
-
-    QModelIndex currentIndex = d->ImagePreview->currentImageIndex();
-
-    if(currentIndex.isValid())
-      {
-      ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
-
-      if(model)
-        {
-        QModelIndex seriesIndex = currentIndex.parent();
-        QModelIndex studyIndex = seriesIndex.parent();
-
-        int seriesCount = model->rowCount(studyIndex);
-        int seriesID = seriesIndex.row();
-
-        seriesID--;
-        if(seriesID < 0) seriesID += seriesCount;
-
-        QModelIndex prevIndex = seriesIndex.sibling(seriesID, 0);
-
-        d->ImagePreview->onModelSelected(prevIndex);
-        d->ThumbnailsWidget->selectThumbnailFromIndex(prevIndex);
-        }
-      }
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onNextStudy(){
-    Q_D(ctkDICOMBrowser);
-
-    QModelIndex currentIndex = d->ImagePreview->currentImageIndex();
-
-    if(currentIndex.isValid())
-      {
-      ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
-
-      if(model)
-        {
-        QModelIndex seriesIndex = currentIndex.parent();
-        QModelIndex studyIndex = seriesIndex.parent();
-        QModelIndex patientIndex = studyIndex.parent();
-
-        int studyCount = model->rowCount(patientIndex);
-        int studyID = studyIndex.row();
-
-        studyID = (studyID + 1)%studyCount;
-
-        QModelIndex nextIndex = studyIndex.sibling(studyID, 0);
-
-        d->ImagePreview->onModelSelected(nextIndex);
-        d->ThumbnailsWidget->selectThumbnailFromIndex(nextIndex);
-        }
-      }
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onPreviousStudy(){
-    Q_D(ctkDICOMBrowser);
-
-    QModelIndex currentIndex = d->ImagePreview->currentImageIndex();
-
-    if(currentIndex.isValid())
-      {
-      ctkDICOMModel* model = const_cast<ctkDICOMModel*>(qobject_cast<const ctkDICOMModel*>(currentIndex.model()));
-
-      if(model)
-        {
-        QModelIndex seriesIndex = currentIndex.parent();
-        QModelIndex studyIndex = seriesIndex.parent();
-        QModelIndex patientIndex = studyIndex.parent();
-
-        int studyCount = model->rowCount(patientIndex);
-        int studyID = studyIndex.row();
-
-        studyID--;
-        if(studyID < 0) studyID += studyCount;
-
-        QModelIndex prevIndex = studyIndex.sibling(studyID, 0);
-
-        d->ImagePreview->onModelSelected(prevIndex);
-        d->ThumbnailsWidget->selectThumbnailFromIndex(prevIndex);
-        }
-      }
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onTreeCollapsed(const QModelIndex &index){
-    Q_UNUSED(index);
-    Q_D(ctkDICOMBrowser);
-    d->TreeView->resizeColumnToContents(0);
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onTreeExpanded(const QModelIndex &index){
-    Q_UNUSED(index);
-    Q_D(ctkDICOMBrowser);
-    d->TreeView->resizeColumnToContents(0);
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onAutoPlayCheckboxStateChanged(int state){
-    Q_D(ctkDICOMBrowser);
-
-    if(state == 0) //OFF
-      {
-      disconnect(d->AutoPlayTimer, SIGNAL(timeout()), this, SLOT(onAutoPlayTimer()));
-      d->AutoPlayTimer->deleteLater();
-      }
-    else if(state == 2) //ON
-      {
-      d->AutoPlayTimer = new QTimer(this);
-      connect(d->AutoPlayTimer, SIGNAL(timeout()), this, SLOT(onAutoPlayTimer()));
-      d->AutoPlayTimer->start(50);
-      }
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onAutoPlayTimer(){
-    this->onNextImage();
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onThumbnailWidthSliderValueChanged(int val){
-  Q_D(ctkDICOMBrowser);
-  d->ThumbnailsWidget->setThumbnailSize(QSize(val, val));
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onSearchParameterChanged(){
-  Q_D(ctkDICOMBrowser);
-  d->DICOMModel.setDatabase(d->DICOMDatabase->database(), d->SearchOption->parameters());
-
-  this->onModelSelected(d->DICOMModel.index(0,0));
-  d->ThumbnailsWidget->clearThumbnails();
-  d->ThumbnailsWidget->addThumbnails(d->DICOMModel.index(0,0));
-  d->ImagePreview->clearImages();
-  d->ImagePreview->onModelSelected(d->DICOMModel.index(0,0));
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onImagePreviewDisplayed(int imageID, int count){
-  Q_D(ctkDICOMBrowser);
-
-  d->PlaySlider->setMinimum(0);
-  d->PlaySlider->setMaximum(count-1);
-  d->PlaySlider->setValue(imageID);
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onSearchPopUpButtonClicked(){
-  Q_D(ctkDICOMBrowser);
-
-  if(d->SearchDockWidget->isFloating())
-    {
-    d->SearchDockWidget->hide();
-    d->SearchDockWidget->setFloating(false);
-    }
-  else
-    {
-    d->SearchDockWidget->setFloating(true);
-    d->SearchDockWidget->adjustSize();
-    d->SearchDockWidget->show();
-    }
-}
-
-//----------------------------------------------------------------------------
-void ctkDICOMBrowser::onSearchWidgetTopLevelChanged(bool topLevel){
+void ctkDICOMBrowser::onModelSelected(const QItemSelection &item1, const QItemSelection &item2)
+{
+  Q_UNUSED(item1);
+  Q_UNUSED(item2);
   Q_D(ctkDICOMBrowser);
-
-  if(topLevel)
-    {
-    d->SearchDockWidget->show();
-    }
-  else
-    {
-    d->SearchDockWidget->hide();
-    }
+  d->ActionRemove->setEnabled(true);
 }

+ 6 - 51
Libs/DICOM/Widgets/ctkDICOMBrowser.h

@@ -22,6 +22,7 @@
 #define __ctkDICOMBrowser_h
 
 // Qt includes 
+#include <QItemSelection>
 #include <QWidget>
 
 #include "ctkDICOMWidgetsExport.h"
@@ -30,6 +31,7 @@ class ctkDICOMBrowserPrivate;
 class ctkThumbnailLabel;
 class QModelIndex;
 class ctkDICOMDatabase;
+class ctkDICOMTableManager;
 
 /// \ingroup DICOM_Widgets
 class CTK_DICOM_WIDGETS_EXPORT ctkDICOMBrowser : public QWidget
@@ -37,9 +39,9 @@ class CTK_DICOM_WIDGETS_EXPORT ctkDICOMBrowser : public QWidget
   Q_OBJECT
   Q_PROPERTY(ctkDICOMDatabase* database READ database)
   Q_PROPERTY(QString databaseDirectory READ databaseDirectory WRITE setDatabaseDirectory)
-  Q_PROPERTY(bool searchWidgetPopUpMode READ searchWidgetPopUpMode WRITE setSearchWidgetPopUpMode)
   Q_PROPERTY(QStringList tagsToPrecache READ tagsToPrecache WRITE setTagsToPrecache)
   Q_PROPERTY(bool displayImportSummary READ displayImportSummary WRITE setDisplayImportSummary)
+  Q_PROPERTY(ctkDICOMTableManager* dicomTableManager READ dicomTableManager)
 
 public:
   typedef QWidget Superclass;
@@ -61,13 +63,10 @@ public:
   /// Also provides a dialog box for progress
   void updateDatabaseSchemaIfNeeded();
 
-  /// Setting search widget pop-up mode
-  /// Default value is false. Setting it to true will make
-  /// search widget to be displayed as pop-up widget
-  void setSearchWidgetPopUpMode(bool flag);
-  bool searchWidgetPopUpMode();
   ctkDICOMDatabase* database();
 
+  ctkDICOMTableManager* dicomTableManager();
+
   /// Option to show or not import summary dialog.
   /// Since the summary dialog is modal, we give the option
   /// of disabling it for batch modes or testing.
@@ -88,10 +87,6 @@ public Q_SLOTS:
   void openQueryDialog();
   void onRemoveAction();
 
-  void suspendModel();
-  void resumeModel();
-  void resetModel();
-
   /// Import a directory - this is used when the user selects a directory
   /// from the Import Dialog, but can also be used externally to trigger
   /// an import (i.e. for testing or to support drag-and-drop)
@@ -115,51 +110,11 @@ Q_SIGNALS:
 protected:
     QScopedPointer<ctkDICOMBrowserPrivate> d_ptr;
 protected Q_SLOTS:
-    void onModelSelected(const QModelIndex& index);
-
-    /// To be called when a thumbnail in thumbnail list widget is selected
-    void onThumbnailSelected(const ctkThumbnailLabel& widget);
-
-    /// To be called when a thumbnail in thumbnail list widget is double-clicked
-    void onThumbnailDoubleClicked(const ctkThumbnailLabel& widget);
+    void onModelSelected(const QItemSelection&, const QItemSelection&);
 
-    /// To be called when previous and next buttons are clicked
-    void onNextImage();
-    void onPreviousImage();
-    void onNextSeries();
-    void onPreviousSeries();
-    void onNextStudy();
-    void onPreviousStudy();
     /// To be called when dialog finishes
     void onQueryRetrieveFinished();
 
-    /// To be called when an entry of the tree list is collapsed
-    void onTreeCollapsed(const QModelIndex& index);
-
-    /// To be called when an entry of the tree list is expanded
-    void onTreeExpanded(const QModelIndex& index);
-
-    /// To be called when auto-play checkbox state changed
-    void onAutoPlayCheckboxStateChanged(int state);
-
-    /// Called by timer for auto-play functionality
-    void onAutoPlayTimer();
-
-    /// To be called when the value of thumbnail size slider bar is changed
-    void onThumbnailWidthSliderValueChanged(int val);
-
-    /// To be called when search parameters in query widget changed
-    void onSearchParameterChanged();
-
-    /// To be called after image preview displayed an image
-    void onImagePreviewDisplayed(int imageID, int count);
-
-private Q_SLOTS:
-
-    void onSearchPopUpButtonClicked();
-
-    void onSearchWidgetTopLevelChanged(bool topLevel);
-
 private:
   Q_DECLARE_PRIVATE(ctkDICOMBrowser);
   Q_DISABLE_COPY(ctkDICOMBrowser);

+ 230 - 0
Libs/DICOM/Widgets/ctkDICOMTableManager.cpp

@@ -0,0 +1,230 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// CTK includes
+#include "ctkDICOMTableManager.h"
+#include "ctkDICOMTableView.h"
+#include "ui_ctkDICOMTableManager.h"
+
+// Qt includes
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QSplitter>
+
+class ctkDICOMTableManagerPrivate : public Ui_ctkDICOMTableManager
+{
+  Q_DECLARE_PUBLIC(ctkDICOMTableManager)
+
+protected:
+  ctkDICOMTableManager* const q_ptr;
+
+public:
+  ctkDICOMTableManagerPrivate(ctkDICOMTableManager& obj);
+  ~ctkDICOMTableManagerPrivate();
+
+  void init();
+  void setCTKDICOMDatabase(ctkDICOMDatabase *db);
+
+  ctkDICOMDatabase* dicomDatabase;
+};
+
+//------------------------------------------------------------------------------
+
+ctkDICOMTableManagerPrivate::ctkDICOMTableManagerPrivate(ctkDICOMTableManager &obj)
+  : q_ptr(&obj)
+{
+
+}
+
+//------------------------------------------------------------------------------
+
+ctkDICOMTableManagerPrivate::~ctkDICOMTableManagerPrivate()
+{
+
+}
+
+//------------------------------------------------------------------------------
+
+void ctkDICOMTableManagerPrivate::init()
+{
+  //setup UI
+  Q_Q(ctkDICOMTableManager);
+  this->setupUi(q);
+
+  this->patientsTable->setQueryTableName("Patients");
+  this->studiesTable->setQueryTableName("Studies");
+  this->studiesTable->setQueryForeignKey("PatientsUID");
+  this->seriesTable->setQueryTableName("Series");
+  this->seriesTable->setQueryForeignKey("StudyInstanceUID");
+
+  // For propagating patient selection changes
+  QObject::connect(this->patientsTable, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
+                   q, SIGNAL(patientsSelectionChanged(const QItemSelection&, const QItemSelection&)));
+  QObject::connect(this->patientsTable, SIGNAL(selectionChanged(const QStringList&)),
+                   q, SIGNAL(patientsSelectionChanged(const QStringList&)));
+
+  // For propagating study selection changes
+  QObject::connect(this->studiesTable, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
+                   q, SIGNAL(studiesSelectionChanged(const QItemSelection&, const QItemSelection&)));
+  QObject::connect(this->studiesTable, SIGNAL(selectionChanged(const QStringList&)),
+                   q, SIGNAL(studiesSelectionChanged(const QStringList&)));
+
+  // For propagating series selection changes
+  QObject::connect(this->seriesTable, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
+                   q, SIGNAL(seriesSelectionChanged(const QItemSelection&, const QItemSelection&)));
+  QObject::connect(this->seriesTable, SIGNAL(selectionChanged(const QStringList&)),
+                   q, SIGNAL(seriesSelectionChanged(const QStringList&)));
+
+  QObject::connect(this->seriesTable, SIGNAL(doubleClicked(const QModelIndex&)),
+                   q, SIGNAL(seriesDoubleClicked(const QModelIndex&)));
+}
+
+//------------------------------------------------------------------------------
+
+void ctkDICOMTableManagerPrivate::setCTKDICOMDatabase(ctkDICOMDatabase* db)
+{
+  this->patientsTable->setDicomDataBase(db);
+  this->studiesTable->setDicomDataBase(db);
+  this->seriesTable->setDicomDataBase(db);
+  this->dicomDatabase = db;
+}
+
+//----------------------------------------------------------------------------
+// ctkDICOMTableManager methods
+
+//----------------------------------------------------------------------------
+
+ctkDICOMTableManager::ctkDICOMTableManager(QWidget *parent)
+  :Superclass(parent)
+  , d_ptr(new ctkDICOMTableManagerPrivate(*this))
+{
+  Q_D(ctkDICOMTableManager);
+  d->init();
+}
+
+//------------------------------------------------------------------------------
+
+ctkDICOMTableManager::ctkDICOMTableManager(ctkDICOMDatabase *db, QWidget *parent)
+  : Superclass(parent)
+  , d_ptr(new ctkDICOMTableManagerPrivate(*this))
+{
+  Q_D(ctkDICOMTableManager);
+  d->init();
+  d->setCTKDICOMDatabase(db);
+}
+
+//------------------------------------------------------------------------------
+
+ctkDICOMTableManager::~ctkDICOMTableManager()
+{
+
+}
+
+//------------------------------------------------------------------------------
+
+void ctkDICOMTableManager::setCTKDICOMDatabase(ctkDICOMDatabase* db)
+{
+  Q_D(ctkDICOMTableManager);
+  d->setCTKDICOMDatabase(db);
+}
+
+//------------------------------------------------------------------------------
+
+void ctkDICOMTableManager::setTableOrientation(const Qt::Orientation &o) const
+{
+  Q_D(const ctkDICOMTableManager);
+  d->tableSplitter->setOrientation(o);
+}
+
+//------------------------------------------------------------------------------
+
+Qt::Orientation ctkDICOMTableManager::tableOrientation()
+{
+  Q_D(ctkDICOMTableManager);
+  return d->tableSplitter->orientation();
+}
+
+//------------------------------------------------------------------------------
+
+QStringList ctkDICOMTableManager::currentPatientsSelection()
+{
+  Q_D(ctkDICOMTableManager);
+  return d->patientsTable->currentSelection();
+}
+
+QStringList ctkDICOMTableManager::currentStudiesSelection()
+{
+  Q_D(ctkDICOMTableManager);
+  return d->studiesTable->currentSelection();
+}
+
+QStringList ctkDICOMTableManager::currentSeriesSelection()
+{
+  Q_D(ctkDICOMTableManager);
+  return d->seriesTable->currentSelection();
+}
+
+void ctkDICOMTableManager::onPatientsQueryChanged(const QStringList &uids)
+{
+  Q_D(ctkDICOMTableManager);
+  const std::pair<QString, QStringList> patientCondition("Patients.UID", uids);
+  d->seriesTable->addSqlWhereCondition(patientCondition);
+  d->studiesTable->addSqlWhereCondition(patientCondition);
+}
+
+void ctkDICOMTableManager::onStudiesQueryChanged(const QStringList &uids)
+{
+  Q_D(ctkDICOMTableManager);
+  const std::pair<QString, QStringList> studiesCondition("Studies.StudyInstanceUID", uids);
+  d->seriesTable->addSqlWhereCondition(studiesCondition);
+}
+
+void ctkDICOMTableManager::onPatientsSelectionChanged(const QStringList &uids)
+{
+  std::pair<QString, QStringList> patientCondition;
+  patientCondition.first = "Patients.UID";
+  Q_D(ctkDICOMTableManager);
+  if (!uids.empty())
+    {
+      patientCondition.second = uids;
+    }
+  else
+    {
+      patientCondition.second = d->patientsTable->uidsForAllRows();
+    }
+  d->studiesTable->addSqlWhereCondition(patientCondition);
+  d->seriesTable->addSqlWhereCondition(patientCondition);
+}
+
+void ctkDICOMTableManager::onStudiesSelectionChanged(const QStringList &uids)
+{
+  std::pair<QString, QStringList> studiesCondition;
+  studiesCondition.first = "Studies.StudyInstanceUID";
+  Q_D(ctkDICOMTableManager);
+  if (!uids.empty())
+    {
+      studiesCondition.second = uids;
+    }
+  else
+    {
+      studiesCondition.second = d->studiesTable->uidsForAllRows();
+    }
+  d->seriesTable->addSqlWhereCondition(studiesCondition);
+}

+ 99 - 0
Libs/DICOM/Widgets/ctkDICOMTableManager.h

@@ -0,0 +1,99 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkDICOMTableManager_h
+#define __ctkDICOMTableManager_h
+
+#include "ctkDICOMWidgetsExport.h"
+#include "ctkDICOMDatabase.h"
+
+// Qt includes
+#include <QWidget>
+#include <QSharedPointer>
+
+class ctkDICOMTableManagerPrivate;
+class ctkDICOMDatabase;
+
+class QItemSelection;
+class QModelIndex;
+
+/// \ingroup DICOM_Widgets
+
+class CTK_DICOM_WIDGETS_EXPORT ctkDICOMTableManager : public QWidget
+{
+  Q_OBJECT
+
+  /**
+    * Property for setting the table layout either to vertical or horizontal alignment
+    */
+  Q_PROPERTY(Qt::Orientation tableOrientation READ tableOrientation WRITE setTableOrientation)
+
+public:
+  typedef QWidget Superclass;
+
+  explicit ctkDICOMTableManager(QWidget* parent = 0);
+  ctkDICOMTableManager(ctkDICOMDatabase* db, QWidget* parent = 0);
+  virtual ~ctkDICOMTableManager();
+
+  /**
+   * @brief Set the ctkDICOMDatabase
+   * @param db the dicom database which should be used
+   */
+  void setCTKDICOMDatabase(ctkDICOMDatabase* db);
+
+  void setTableOrientation(const Qt::Orientation&) const;
+  Qt::Orientation tableOrientation();
+
+  /**
+   * @brief Get the current selection of the dicomTableViews
+   * @return a list with the uids of the selected items
+   */
+  QStringList currentPatientsSelection();
+  QStringList currentStudiesSelection();
+  QStringList currentSeriesSelection();
+
+public Q_SLOTS:
+
+  void onPatientsQueryChanged(const QStringList&);
+  void onStudiesQueryChanged(const QStringList&);
+  void onPatientsSelectionChanged(const QStringList&);
+  void onStudiesSelectionChanged(const QStringList&);
+
+Q_SIGNALS:
+  /// Signals for propagating selection changes of the different tables
+  void patientsSelectionChanged(const QItemSelection&, const QItemSelection&);
+  void studiesSelectionChanged(const QItemSelection&, const QItemSelection&);
+  void seriesSelectionChanged(const QItemSelection&, const QItemSelection&);
+
+  void patientsSelectionChanged(const QStringList &uids);
+  void studiesSelectionChanged(const QStringList &uids);
+  void seriesSelectionChanged(const QStringList &uids);
+
+  void seriesDoubleClicked(const QModelIndex&);
+
+protected:
+  QScopedPointer<ctkDICOMTableManagerPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkDICOMTableManager)
+  Q_DISABLE_COPY(ctkDICOMTableManager)
+};
+
+#endif // __ctkDICOMTableManager_h

+ 356 - 0
Libs/DICOM/Widgets/ctkDICOMTableView.cpp

@@ -0,0 +1,356 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// ctkDICOMWidget includes
+#include "ctkDICOMTableView.h"
+#include "ui_ctkDICOMTableView.h"
+
+// Qt includes
+#include <QSortFilterProxyModel>
+#include <QSqlQueryModel>
+
+//------------------------------------------------------------------------------
+class ctkDICOMTableViewPrivate : public Ui_ctkDICOMTableView
+{
+  Q_DECLARE_PUBLIC (ctkDICOMTableView)
+
+protected:
+  ctkDICOMTableView* const q_ptr;
+
+public:
+  ctkDICOMTableViewPrivate(ctkDICOMTableView& obj);
+  ctkDICOMTableViewPrivate(ctkDICOMTableView& obj, ctkDICOMDatabase* db);
+  ~ctkDICOMTableViewPrivate();
+  // Initialize UI
+  void init();
+  // Setup tableview with tablemodel if database is available
+  void setUpTableView();
+  //Temporay solution to hide UID columns
+  void hideUIDColumns();
+
+  void showFilterActiveWarning(bool);
+
+  QString queryTableName() const;
+
+  ctkDICOMDatabase* dicomDatabase;
+  QSqlQueryModel dicomSQLModel;
+  QSortFilterProxyModel* dicomSQLFilterModel;
+  QString queryForeignKey;
+
+  QStringList currentSelection;
+  //Key = QString for columns, Values = QStringList
+  QHash<QString, QStringList> sqlWhereConditions;
+
+};
+
+//------------------------------------------------------------------------------
+ctkDICOMTableViewPrivate::ctkDICOMTableViewPrivate(ctkDICOMTableView &obj)
+  : q_ptr(&obj)
+{
+  this->dicomSQLFilterModel = new QSortFilterProxyModel(&obj);
+  this->dicomDatabase = new ctkDICOMDatabase(&obj);
+}
+
+//------------------------------------------------------------------------------
+ctkDICOMTableViewPrivate::ctkDICOMTableViewPrivate(ctkDICOMTableView &obj, ctkDICOMDatabase* db)
+  : q_ptr(&obj)
+  , dicomDatabase(db)
+{
+  this->dicomSQLFilterModel = new QSortFilterProxyModel(&obj);
+}
+
+//------------------------------------------------------------------------------
+ctkDICOMTableViewPrivate::~ctkDICOMTableViewPrivate()
+{
+}
+
+void ctkDICOMTableViewPrivate::init()
+{
+  Q_Q(ctkDICOMTableView);
+  this->setupUi(q);
+
+  this->leSearchBox->setAlwaysShowClearIcon(true);
+  this->leSearchBox->setShowSearchIcon(true);
+
+  if (this->dicomDatabase != 0)
+    {
+      this->setUpTableView();
+    }
+}
+
+//------------------------------------------------------------------------------
+void ctkDICOMTableViewPrivate::setUpTableView()
+{
+  Q_Q(ctkDICOMTableView);
+  if (this->dicomDatabase != 0)
+    {
+      q->setQuery();
+      this->dicomSQLFilterModel->setSourceModel(&this->dicomSQLModel);
+      this->dicomSQLFilterModel->setFilterKeyColumn(-1);
+      this->dicomSQLFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+      this->tblDicomDatabaseView->setModel(this->dicomSQLFilterModel);
+      this->tblDicomDatabaseView->setColumnHidden(0, true);
+      this->tblDicomDatabaseView->setSortingEnabled(true);
+      this->tblDicomDatabaseView->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
+      this->hideUIDColumns();
+
+      QObject::connect(this->tblDicomDatabaseView->selectionModel(),
+                       SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),
+                       q, SLOT(onSelectionChanged()));
+
+      QObject::connect(this->tblDicomDatabaseView->selectionModel(),
+                       SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),
+                       q, SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)));
+
+      QObject::connect(this->tblDicomDatabaseView, SIGNAL(doubleClicked(const QModelIndex&)),
+                       q, SIGNAL(doubleClicked(const QModelIndex&)));
+
+      QObject::connect(this->leSearchBox, SIGNAL(textChanged(QString)),
+                       this->dicomSQLFilterModel, SLOT(setFilterWildcard(QString)));
+
+      QObject::connect(this->leSearchBox, SIGNAL(textChanged(QString)), q, SLOT(onFilterChanged()));
+
+      QObject::connect(this->dicomDatabase, SIGNAL(schemaUpdated()), q, SLOT(onDatabaseChanged()));
+
+      QObject::connect(this->dicomDatabase, SIGNAL(databaseChanged()), q, SLOT(onDatabaseChanged()));
+    }
+}
+
+//------------------------------------------------------------------------------
+//Temporay solution to hide UID columns
+void ctkDICOMTableViewPrivate::hideUIDColumns()
+{
+  const int numberOfColumns = this->tblDicomDatabaseView->model()->columnCount();
+  for (int i = 0; i < numberOfColumns; ++i)
+    {
+    QString columnName = this->tblDicomDatabaseView->model()->headerData(i, Qt::Horizontal).toString();
+    if (columnName.contains("UID"))
+      {
+        this->tblDicomDatabaseView->hideColumn(i);
+      }
+    }
+}
+
+//----------------------------------------------------------------------------
+QString ctkDICOMTableViewPrivate::queryTableName() const
+{
+  return this->lblTableName->text();
+}
+
+//----------------------------------------------------------------------------
+void ctkDICOMTableViewPrivate::showFilterActiveWarning(bool showWarning)
+{
+  QPalette palette;
+  if (showWarning)
+    {
+      palette.setColor(QPalette::Base,Qt::yellow);
+    }
+  else
+    {
+      palette.setColor(QPalette::Base,Qt::white);
+    }
+  this->leSearchBox->setPalette(palette);
+}
+
+
+//----------------------------------------------------------------------------
+// ctkDICOMTableView methods
+
+//----------------------------------------------------------------------------
+ctkDICOMTableView::ctkDICOMTableView(QWidget *parent)
+  : Superclass(parent)
+  , d_ptr(new ctkDICOMTableViewPrivate(*this))
+{
+  Q_D(ctkDICOMTableView);
+  d->init();
+}
+
+//----------------------------------------------------------------------------
+ctkDICOMTableView::ctkDICOMTableView(QString queryTableName, QWidget *parent)
+  : Superclass(parent)
+  , d_ptr(new ctkDICOMTableViewPrivate(*this))
+{
+  Q_D(ctkDICOMTableView);
+  d->init();
+  this->setQueryTableName(queryTableName);
+  d->lblTableName->setText(queryTableName);
+}
+
+//------------------------------------------------------------------------------
+ctkDICOMTableView::ctkDICOMTableView (ctkDICOMDatabase* dicomDataBase, QString queryTableName, QWidget* parent)
+  : Superclass(parent)
+  , d_ptr(new ctkDICOMTableViewPrivate(*this))
+{
+  this->setDicomDataBase(dicomDataBase);
+  Q_D(ctkDICOMTableView);
+  this->setQueryTableName(queryTableName);
+  d->init();
+}
+
+//------------------------------------------------------------------------------
+ctkDICOMTableView::~ctkDICOMTableView()
+{
+}
+
+//------------------------------------------------------------------------------
+void ctkDICOMTableView::setDicomDataBase(ctkDICOMDatabase *dicomDatabase)
+{
+  Q_D(ctkDICOMTableView);
+  d->dicomDatabase = dicomDatabase;
+  //Create connections for new database
+  QObject::connect(d->dicomDatabase, SIGNAL(schemaUpdated()), this, SLOT(onDatabaseChanged()));
+  QObject::connect(d->dicomDatabase, SIGNAL(databaseChanged()), this, SLOT(onDatabaseChanged()));
+  this->setQuery();
+  d->hideUIDColumns();
+}
+
+//------------------------------------------------------------------------------
+void ctkDICOMTableView::setQueryTableName(const QString &tableName)
+{
+  Q_D(ctkDICOMTableView);
+  d->lblTableName->setText(tableName);
+}
+
+//------------------------------------------------------------------------------
+void ctkDICOMTableView::setQueryForeignKey(const QString &foreignKey)
+{
+  Q_D(ctkDICOMTableView);
+  d->queryForeignKey = foreignKey;
+}
+
+//------------------------------------------------------------------------------
+void ctkDICOMTableView::onSelectionChanged()
+{
+  emit selectionChanged(currentSelection());
+}
+
+//------------------------------------------------------------------------------
+void ctkDICOMTableView::onDatabaseChanged()
+{
+  Q_D(ctkDICOMTableView);
+  setQuery();
+}
+
+//------------------------------------------------------------------------------
+void ctkDICOMTableView::onUpdateQuery(const QStringList& uids)
+{
+  Q_D(ctkDICOMTableView);
+
+  setQuery(uids);
+
+  d->showFilterActiveWarning( d->dicomSQLFilterModel->rowCount() == 0 &&
+                              d->leSearchBox->text().length() != 0 );
+
+  const QStringList& newUIDS = this->uidsForAllRows();
+  emit queryChanged(newUIDS);
+}
+
+//------------------------------------------------------------------------------
+void ctkDICOMTableView::onFilterChanged()
+{
+  Q_D(ctkDICOMTableView);
+
+  const QStringList uids = this->uidsForAllRows();
+
+  d->showFilterActiveWarning( d->dicomSQLFilterModel->rowCount() == 0 );
+
+  d->tblDicomDatabaseView->clearSelection();
+  emit queryChanged(uids);
+}
+
+//------------------------------------------------------------------------------
+void ctkDICOMTableView::setQuery(const QStringList &uids)
+{
+  Q_D(ctkDICOMTableView);
+  QString query = ("select distinct %1.* from Patients, Series, Studies where "
+                   "Patients.UID = Studies.PatientsUID and Studies.StudyInstanceUID = Series.StudyInstanceUID");
+
+  if (!uids.empty() && d->queryForeignKey.length() != 0)
+    {
+      query += " and %1."+d->queryForeignKey+" in ( '";
+      query.append(uids.join("','")).append("')");
+    }
+  if (!d->sqlWhereConditions.empty())
+    {
+      QHash<QString, QStringList>::const_iterator i = d->sqlWhereConditions.begin();
+      while (i != d->sqlWhereConditions.end())
+        {
+          if (!i.value().empty())
+            {
+              query += " and "+i.key()+" in ( '";
+              query.append(i.value().join("','")).append("')");
+            }
+          ++i;
+        }
+    }
+  if (d->dicomDatabase != 0)
+    d->dicomSQLModel.setQuery(query.arg(d->queryTableName()), d->dicomDatabase->database());
+}
+
+void ctkDICOMTableView::addSqlWhereCondition(const std::pair<QString, QStringList> &condition)
+{
+  Q_D(ctkDICOMTableView);
+  d->sqlWhereConditions.insert(condition.first, condition.second);
+}
+
+//------------------------------------------------------------------------------
+QStringList ctkDICOMTableView::uidsForAllRows() const
+{
+  Q_D(const ctkDICOMTableView);
+  QAbstractItemModel* tableModel = d->tblDicomDatabaseView->model();
+  int numberOfRows = tableModel->rowCount();
+  QStringList uids;
+  if (numberOfRows == 0)
+    {
+      //Return invalid UID if there are no rows
+      uids << QString("#");
+    }
+  else
+    {
+      for(int i = 0; i < numberOfRows; ++i)
+        {
+          uids << QString("%1").arg(tableModel->index(i,0).data().toString());
+        }
+    }
+  return uids;
+}
+
+//------------------------------------------------------------------------------
+QStringList ctkDICOMTableView::currentSelection() const
+{
+  Q_D(const ctkDICOMTableView);
+
+  QModelIndexList currentSelection = d->tblDicomDatabaseView->selectionModel()->selectedRows(0);
+  QStringList uids;
+
+  foreach(QModelIndex i, currentSelection)
+    {
+      uids<< i.data().toString();
+    }
+
+  return  uids;
+}
+
+//------------------------------------------------------------------------------
+bool ctkDICOMTableView::filterActive()
+{
+  Q_D(ctkDICOMTableView);
+  return (d->leSearchBox->text().length() != 0);
+}

+ 170 - 0
Libs/DICOM/Widgets/ctkDICOMTableView.h

@@ -0,0 +1,170 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0.txt
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkDICOMTableView_h
+#define __ctkDICOMTableView_h
+
+// Qt includes
+#include <QItemSelection>
+#include <QWidget>
+
+// ctkDICOMCore includes
+#include "ctkDICOMDatabase.h"
+#include "ctkDICOMWidgetsExport.h"
+
+class ctkDICOMTableViewPrivate;
+
+/**
+ * @brief The ctkDICOMTableView displays the content of a specific table of the ctkDICOMDatabase
+ *
+ * The ctkDICOMTableView holds a QTableView which displays the content of the selected
+ * ctkDICOMDatabase. It also holds a ctkSearchBox which allows filtering of the table content.
+ *
+ * @ingroup DICOM_Widgets
+ */
+class CTK_DICOM_WIDGETS_EXPORT ctkDICOMTableView : public QWidget
+{
+  Q_OBJECT
+  Q_PROPERTY(bool filterActive READ filterActive)
+
+public:
+  typedef QWidget Superclass;
+
+  /**
+   * Constructs ctkDICOMTableView without underlying database and table name
+   * @param parent parentwidget
+   */
+  explicit ctkDICOMTableView(QWidget* parent = 0);
+  /**
+   * ctor with tablename as parameter
+   * @param parent the parent widget
+   * @param queryTableName the name of the table of the ctkDICOMDatabase which shall be displayed
+   */
+  explicit ctkDICOMTableView(QString queryTableName, QWidget* parent = 0);
+
+  /**
+   * ctor with tablename and database as parameter
+   * @param ctkDicomDataBase the ctkDICOMDatabase which shall be used
+   * @param parent the parent widget
+   * @param queryTableName the name of the table of the ctkDICOMDatabase which shall be displayed
+   */
+  explicit ctkDICOMTableView (ctkDICOMDatabase* dicomDataBase, QString queryTableName, QWidget* parent = 0);
+
+  virtual ~ctkDICOMTableView();
+
+  /**
+   * @brief Setting the ctkDICOMDatabase which shall be queried
+   * @param dicomDataBase the underlying database
+   */
+  void setDicomDataBase(ctkDICOMDatabase* dicomDatabase);
+
+  /**
+   * Setting the table name which shall be used for the database query
+   * @param tableName the name of the database table
+   */
+  void setQueryTableName(const QString &tableName);
+
+  /**
+   * Setting the foreign key for the database query. This is usefull if e.g. you
+   * want to select the studies for a certain patient
+   * @param foreignKey the foreign key which will be used for the query
+   */
+  void setQueryForeignKey(const QString &foreignKey);
+
+  /**
+   * Set the query for the underlying database. If the uid list is not empty just the
+   * entries with the according uids are selected
+   * @param uids a list of uids which should be selected
+   */
+  void setQuery (const QStringList &uids = QStringList());
+
+  /**
+   * @brief Add a where condition to the usual select statement
+   * @param condition std::pair with column name and a value list
+   */
+  void addSqlWhereCondition(const std::pair<QString, QStringList>& condition);
+
+  /**
+   * @brief Returns the uids of the current selected rows
+   * @return a list containing all the uids of the selected rows
+   */
+  QStringList currentSelection() const;
+
+  /**
+   * @brief Getting the UIDs for all rows
+   * @return a QStringList with the uids for all rows
+   */
+  QStringList uidsForAllRows() const;
+
+  bool filterActive();
+
+public Q_SLOTS:
+
+  /**
+   * @brief slot is called if the selection of the tableview is changed
+   * Within this slot the signal signalSelectionChanged is emitted
+   */
+  void onSelectionChanged();
+
+  /**
+   * @brief Updates the query which is used for displaying the table content
+   * @param uids the uids of the table entries which shall be displayed
+   */
+  void onUpdateQuery(const QStringList &uids);
+
+protected Q_SLOTS:
+  /**
+   * @brief Called when the underlying database changes
+   */
+  void onDatabaseChanged();
+
+  /**
+   * @brief Called when the text of the ctkSearchBox has changed
+   */
+  void onFilterChanged();
+
+Q_SIGNALS:
+  /**
+   * @brief Is emitted when the selection in the tableview has changed
+   * @param uids the list of uids of the selected objects
+   */
+  void selectionChanged(const QStringList &uids);
+
+  /**
+   * @brief Is emitted when the data selection has changed
+   */
+  void selectionChanged(const QItemSelection&,const QItemSelection&);
+
+  /**
+   * @brief Is emitted when the query text has changed
+   * @param uids the list of uids of the objects included in the query
+   */
+  void queryChanged(const QStringList &uids);
+
+  void doubleClicked(const QModelIndex&);
+
+protected:
+  QScopedPointer<ctkDICOMTableViewPrivate> d_ptr;
+
+  Q_DECLARE_PRIVATE(ctkDICOMTableView)
+  Q_DISABLE_COPY(ctkDICOMTableView)
+};
+
+#endif // __ctkDICOMTableView_h