Sfoglia il codice sorgente

ENH: Add ctkPathLineEdit to select a file or dir

It's a lineedit with completer, validator and history to quickly select the
path of a file or directory
Julien Finet 14 anni fa
parent
commit
4c56099ddc

+ 3 - 0
Libs/Widgets/CMakeLists.txt

@@ -66,6 +66,8 @@ SET(KIT_SRCS
   ctkMatrixWidget.h
   ctkMenuButton.cpp
   ctkMenuButton.h
+  ctkPathLineEdit.cpp
+  ctkPathLineEdit.h
   ctkPixmapIconEngine.cpp
   ctkPixmapIconEngine.h
   ctkRangeSlider.cpp
@@ -149,6 +151,7 @@ SET(KIT_MOC_SRCS
   ctkMaterialPropertyWidget.h
   ctkMatrixWidget.h
   ctkMenuButton.h
+  ctkPathLineEdit.h
   ctkRangeSlider.h
   ctkRangeWidget.h
   ctkScreenshotDialog.h

+ 3 - 0
Libs/Widgets/Plugins/CMakeLists.txt

@@ -49,6 +49,8 @@ SET(PLUGIN_SRCS
   ctkMatrixWidgetPlugin.h
   ctkMenuButtonPlugin.cpp
   ctkMenuButtonPlugin.h
+  ctkPathLineEditPlugin.cpp
+  ctkPathLineEditPlugin.h
   ctkRangeSliderPlugin.cpp
   ctkRangeSliderPlugin.h
   ctkRangeWidgetPlugin.cpp
@@ -91,6 +93,7 @@ SET(PLUGIN_MOC_SRCS
   ctkMaterialPropertyPreviewLabelPlugin.h
   ctkMatrixWidgetPlugin.h
   ctkMenuButtonPlugin.h
+  ctkPathLineEditPlugin.h
   ctkRangeSliderPlugin.h
   ctkRangeWidgetPlugin.h
   ctkTransferFunctionViewPlugin.h

+ 69 - 0
Libs/Widgets/Plugins/ctkPathLineEditPlugin.cpp

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

+ 44 - 0
Libs/Widgets/Plugins/ctkPathLineEditPlugin.h

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

+ 2 - 0
Libs/Widgets/Plugins/ctkWidgetsPlugins.h

@@ -43,6 +43,7 @@
 #include "ctkMaterialPropertyWidgetPlugin.h"
 #include "ctkMatrixWidgetPlugin.h"
 #include "ctkMenuButtonPlugin.h"
+#include "ctkPathLineEditPlugin.h"
 #include "ctkRangeSliderPlugin.h"
 #include "ctkRangeWidgetPlugin.h"
 #include "ctkSettingsPanelPlugin.h"
@@ -81,6 +82,7 @@ public:
             << new ctkMaterialPropertyWidgetPlugin
             << new ctkMatrixWidgetPlugin
             << new ctkMenuButtonPlugin
+            << new ctkPathLineEditPlugin
             << new ctkRangeSliderPlugin
             << new ctkRangeWidgetPlugin
             << new ctkSettingsPanelPlugin

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

@@ -31,6 +31,7 @@ CREATE_TEST_SOURCELIST(Tests ${KIT}CppTests.cxx
   ctkMatrixWidgetTest1.cpp
   ctkMatrixWidgetTest2.cpp
   ctkMenuButtonTest1.cpp
+  ctkPathLineEditTest1.cpp
   ctkRangeSliderTest1.cpp
   ctkRangeWidgetTest1.cpp
   ctkRangeWidgetTest2.cpp
@@ -97,12 +98,15 @@ SIMPLE_TEST( ctkComboBoxTest1 )
 SIMPLE_TEST( ctkConfirmExitDialogTest1 )
 SIMPLE_TEST( ctkConsoleWidgetTest1 )
 SIMPLE_TEST( ctkCoordinatesWidgetTest1 )
+SIMPLE_TEST( ctkDateRangeWidgetTest1 )
 SIMPLE_TEST( ctkDirectoryButtonTest1 )
 SIMPLE_TEST( ctkDoubleRangeSliderTest1 )
 SIMPLE_TEST( ctkDoubleRangeSliderTest2 )
 SIMPLE_TEST( ctkDoubleSliderTest1 )
 SIMPLE_TEST( ctkDynamicSpacerTest1 )
 SIMPLE_TEST( ctkDynamicSpacerTest2 )
+SIMPLE_TEST( ctkExampleUseOfWorkflowWidgetUsingDerivedSteps )
+SIMPLE_TEST( ctkExampleUseOfWorkflowWidgetUsingSignalsAndSlots )
 SIMPLE_TEST( ctkFileDialogTest1 )
 SIMPLE_TEST( ctkFittedTextBrowserTest1 )
 SIMPLE_TEST( ctkFontButtonTest1 )
@@ -111,17 +115,15 @@ SIMPLE_TEST( ctkMaterialPropertyWidgetTest1 )
 SIMPLE_TEST( ctkMatrixWidgetTest1 )
 SIMPLE_TEST( ctkMatrixWidgetTest2 )
 SIMPLE_TEST( ctkMenuButtonTest1 )
+SIMPLE_TEST( ctkPathLineEditTest1 )
 SIMPLE_TEST( ctkRangeSliderTest1 )
 SIMPLE_TEST( ctkRangeWidgetTest1 )
 SIMPLE_TEST( ctkRangeWidgetTest2 )
-SIMPLE_TEST( ctkDateRangeWidgetTest1 )
+SIMPLE_TEST( ctkSettingsDialogTest1 )
 SIMPLE_TEST( ctkSettingsPanelTest1 )
 SIMPLE_TEST( ctkSettingsTest1 )
-SIMPLE_TEST( ctkSettingsDialogTest1 )
 SIMPLE_TEST( ctkSliderWidgetTest1 )
 SIMPLE_TEST( ctkToolTipTrapperTest1 )
 SIMPLE_TEST( ctkTreeComboBoxTest1 )
 SIMPLE_TEST( ctkWorkflowWidgetTest1 )
 SIMPLE_TEST( ctkWorkflowWidgetTest2 )
-SIMPLE_TEST( ctkExampleUseOfWorkflowWidgetUsingDerivedSteps )
-SIMPLE_TEST( ctkExampleUseOfWorkflowWidgetUsingSignalsAndSlots )

+ 109 - 0
Libs/Widgets/Testing/Cpp/ctkPathLineEditTest1.cpp

@@ -0,0 +1,109 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QApplication>
+#include <QSignalSpy>
+#include <QStyle>
+#include <QTimer>
+#include <QVBoxLayout>
+
+// CTK includes
+#include "ctkPathLineEdit.h"
+
+// STD includes
+#include <cstdlib>
+#include <iostream>
+
+//-----------------------------------------------------------------------------
+int ctkPathLineEditTest1(int argc, char * argv [] )
+{
+  QApplication app(argc, argv);
+
+  QWidget topLevel;
+  ctkPathLineEdit button;
+  ctkPathLineEdit button2("Files",
+                          QStringList() << "Images (*.png *.jpg)" << "Text (*.txt)",
+                          ctkPathLineEdit::Files);
+  ctkPathLineEdit button3("Dirs", QStringList("CTK*"));
+
+  QVBoxLayout* layout = new QVBoxLayout;
+  layout->addWidget(&button);
+  layout->addWidget(&button2);
+  layout->addWidget(&button3);
+  topLevel.setLayout(layout);
+
+  button.setCurrentPath(QDir::tempPath());
+  if (button.currentPath() != QDir::tempPath())
+    {
+    std::cerr << "ctkPathLineEdit::setCurrentPath() failed"
+              << qPrintable(button.currentPath()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  button.setLabel("Default");
+
+  if (button.label() != "Default")
+    {
+    std::cerr << "ctkPathLineEdit::setLabel() failed"
+              << qPrintable(button.label()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  QStringList nameFilters= button2.nameFilters();
+  nameFilters <<  "*.conf";
+  button2.setNameFilters(nameFilters);
+
+  if (button2.nameFilters() != nameFilters)
+    {
+    std::cerr << "ctkPathLineEdit::setNameFilters() failed" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  button2.setFilters(button2.filters() | ctkPathLineEdit::Readable);
+  if (!(button2.filters() & ctkPathLineEdit::Readable))
+    {
+    std::cerr << "ctkPathLineEdit::setFilters() failed"
+              << button2.filters() << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  button2.setCurrentFileExtension("jpg");
+
+  if (!button2.currentPath().endsWith(".jpg"))
+    {
+    std::cerr << "ctkPathLineEdit::setCurrentFileExtension() failed"
+              << qPrintable(button2.currentPath()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  topLevel.show();
+
+  if (argc < 2 || QString(argv[1]) != "-I" )
+    {
+    QTimer::singleShot(500, &app, SLOT(quit()));
+    }
+
+  QTimer::singleShot(100, &button, SLOT(retrieveHistory()));
+  QTimer::singleShot(115, &button2, SLOT(addCurrentPathToHistory()));
+  QTimer::singleShot(120, &button3, SLOT(browse()));
+
+  return app.exec();
+}

+ 405 - 0
Libs/Widgets/ctkPathLineEdit.cpp

@@ -0,0 +1,405 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QComboBox>
+#include <QCompleter>
+#include <QDebug>
+#include <QDirModel>
+#include <QFileDialog>
+#include <QHBoxLayout>
+#include <QLineEdit>
+#include <QRegExp>
+#include <QRegExpValidator>
+#include <QSettings>
+
+// CTK includes
+#include "ctkPathLineEdit.h"
+
+//-----------------------------------------------------------------------------
+const char *qt_file_dialog_filter_reg_exp =
+"^(.*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$";
+
+//-----------------------------------------------------------------------------
+QStringList filterNameExtensions(const QString& filterName)
+{
+  QRegExp regexp(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
+  int i = regexp.indexIn(filterName);
+  if (i < 0)
+    {
+    return QStringList();
+    }
+  QString f = regexp.cap(2);
+  return f.split(QLatin1Char(' '), QString::SkipEmptyParts);
+}
+
+//-----------------------------------------------------------------------------
+QStringList filterNamesExtensions(const QStringList& filterNames)
+{
+  QStringList extensions;
+  foreach(const QString& filterName, filterNames)
+    {
+    extensions << filterNameExtensions(filterName);
+    }
+  return extensions;
+}
+
+//-----------------------------------------------------------------------------
+QString extensionToRegExp(const QString& extension)
+{
+  // typically *.jpg
+  QRegExp extensionExtractor("\\*\\.(\\w+)");
+  int pos = extensionExtractor.indexIn(extension);
+  if (pos < 0)
+    {
+    return QString();
+    }
+  return ".*\\." + extensionExtractor.cap(1) + "?$";
+}
+
+//-----------------------------------------------------------------------------
+QRegExp filterNamesToRegExpExtensions(const QStringList& filterNames)
+{
+  QString pattern;
+  foreach(QString filterName, filterNames)
+    {
+    foreach(QString extension, filterNameExtensions(filterName))
+      {
+      QString regExpExtension = extensionToRegExp(extension);
+      if (!regExpExtension.isEmpty())
+        {
+        if (pattern.isEmpty())
+          {
+          pattern = "(";
+          }
+        else
+          {
+          pattern += "|";
+          }
+        pattern +=regExpExtension;
+        }
+      }
+    }
+  if (pattern.isEmpty())
+    {
+    pattern = ".+";
+    }
+  else
+    {
+    pattern += ")";
+    }
+  return QRegExp(pattern);
+}
+
+//-----------------------------------------------------------------------------
+class ctkPathLineEditPrivate
+{
+  Q_DECLARE_PUBLIC(ctkPathLineEdit);
+
+protected:
+  ctkPathLineEdit* const q_ptr;
+
+public:
+  ctkPathLineEditPrivate(ctkPathLineEdit& object);
+  void init();
+  void updateFilter();
+
+  QComboBox*            ComboBox;
+
+  QString               Label;              //!< used in file dialogs
+  QStringList           NameFilters;        //!< Regular expression (in wildcard mode) used to help the user to complete the line
+  QDir::Filters         Filters;            //!< Type of path (file, dir...)
+
+  bool                  HasValidInput;      //!< boolean that stores the old state of valid input
+
+  static QString        sCurrentDirectory;   //!< Content the last value of the current directory
+  static int            sMaxHistory;     //!< Size of the history, if the history is full and a new value is added, the oldest value is dropped
+};
+
+QString ctkPathLineEditPrivate::sCurrentDirectory = "";
+int ctkPathLineEditPrivate::sMaxHistory = 5;
+
+//-----------------------------------------------------------------------------
+ctkPathLineEditPrivate::ctkPathLineEditPrivate(ctkPathLineEdit& object)
+  :q_ptr(&object)
+{
+  this->ComboBox = 0;
+  this->HasValidInput = false;
+  this->Filters = QDir::AllEntries|QDir::NoDotAndDotDot|QDir::Readable;
+}
+
+//-----------------------------------------------------------------------------
+void ctkPathLineEditPrivate::init()
+{
+  Q_Q(ctkPathLineEdit);
+  this->ComboBox = new QComboBox(q);
+  QHBoxLayout* layout = new QHBoxLayout(q);
+  layout->addWidget(this->ComboBox);
+  layout->setContentsMargins(0,0,0,0);
+
+  this->ComboBox->setEditable(true);
+  q->setSizePolicy(QSizePolicy(
+                     QSizePolicy::Expanding, QSizePolicy::Fixed,
+                     QSizePolicy::LineEdit));
+
+  QObject::connect(this->ComboBox,SIGNAL(editTextChanged(const QString&)),
+                   q, SLOT(setCurrentDirectory(const QString&)));
+  QObject::connect(this->ComboBox,SIGNAL(editTextChanged(const QString&)),
+                   q, SLOT(updateHasValidInput()));
+}
+
+//-----------------------------------------------------------------------------
+void ctkPathLineEditPrivate::updateFilter()
+{
+  Q_Q(ctkPathLineEdit);
+  // help completion for the QComboBox::QLineEdit
+  QCompleter *newCompleter = new QCompleter(q);
+  newCompleter->setModel(new QDirModel(
+                           filterNamesExtensions(this->NameFilters),
+                           this->Filters | QDir::NoDotAndDotDot | QDir::AllDirs,
+                           QDir::Name|QDir::DirsLast, newCompleter));
+  this->ComboBox->setCompleter(newCompleter);
+}
+
+//-----------------------------------------------------------------------------
+ctkPathLineEdit::ctkPathLineEdit(QWidget *parentWidget)
+  : QWidget(parentWidget)
+  , d_ptr(new ctkPathLineEditPrivate(*this))
+{
+  Q_D(ctkPathLineEdit);
+  d->init();
+}
+
+//-----------------------------------------------------------------------------
+ctkPathLineEdit::ctkPathLineEdit(const QString& label,
+                                 const QStringList& nameFilters,
+                                 Filters filters,
+                                 QWidget *parentWidget)
+  : QWidget(parentWidget)
+  , d_ptr(new ctkPathLineEditPrivate(*this))
+{
+  Q_D(ctkPathLineEdit);
+  d->init();
+
+  this->setLabel(label);
+  this->setNameFilters(nameFilters);
+  this->setFilters(filters);
+}
+
+//-----------------------------------------------------------------------------
+ctkPathLineEdit::~ctkPathLineEdit()
+{
+}
+
+//-----------------------------------------------------------------------------
+void ctkPathLineEdit::setLabel(const QString &label)
+{
+  Q_D(ctkPathLineEdit);
+  d->Label = label;
+}
+
+//-----------------------------------------------------------------------------
+const QString& ctkPathLineEdit::label()const
+{
+  Q_D(const ctkPathLineEdit);
+  return d->Label;
+}
+
+//-----------------------------------------------------------------------------
+void ctkPathLineEdit::setNameFilters(const QStringList &nameFilters)
+{
+  Q_D(ctkPathLineEdit);
+  d->NameFilters = nameFilters;
+  d->updateFilter();
+  d->ComboBox->lineEdit()->setValidator(
+    new QRegExpValidator(
+      filterNamesToRegExpExtensions(d->NameFilters), this));
+}
+
+//-----------------------------------------------------------------------------
+const QStringList& ctkPathLineEdit::nameFilters()const
+{
+  Q_D(const ctkPathLineEdit);
+  return d->NameFilters;
+}
+
+//-----------------------------------------------------------------------------
+void ctkPathLineEdit::setFilters(const Filters &filters)
+{
+  Q_D(ctkPathLineEdit);
+  d->Filters = QFlags<QDir::Filter>(static_cast<int>(filters));
+  d->updateFilter();
+}
+
+//-----------------------------------------------------------------------------
+ctkPathLineEdit::Filters ctkPathLineEdit::filters()const
+{
+  Q_D(const ctkPathLineEdit);
+  return QFlags<ctkPathLineEdit::Filter>(static_cast<int>(d->Filters));
+}
+
+//-----------------------------------------------------------------------------
+void ctkPathLineEdit::browse()
+{
+  Q_D(ctkPathLineEdit);
+  QString path = "";
+  if ( d->Filters & QDir::Files ) //file
+    {
+    if ( d->Filters & QDir::Writable) // load or save
+      {
+      path = QFileDialog::getSaveFileName(this,
+        tr("Select a file to save "),
+        this->currentPath().isEmpty() ? ctkPathLineEditPrivate::sCurrentDirectory : this->currentPath(),
+                                          d->NameFilters.join(";;"));
+      }
+    else
+      {
+      path = QFileDialog::getOpenFileName(
+        this,
+        QString("Open a file"),
+        this->currentPath().isEmpty()? ctkPathLineEditPrivate::sCurrentDirectory : this->currentPath(),
+        d->NameFilters.join(";;"));
+      }
+    }
+  else //directory
+    {
+    path = QFileDialog::getExistingDirectory(
+      this,
+      QString("Select a directory..."),
+      this->currentPath().isEmpty() ? ctkPathLineEditPrivate::sCurrentDirectory : this->currentPath());
+    }
+  if (path.isEmpty())
+    {
+    return;
+    }
+  this->setCurrentPath(path);
+}
+
+//-----------------------------------------------------------------------------
+void ctkPathLineEdit::retrieveHistory()
+{
+  Q_D(ctkPathLineEdit);
+  d->ComboBox->clear();
+  // fill the combobox using the QSettings
+  QSettings settings;
+  QString key = "ctkPathLineEdit/" + this->objectName();
+  QStringList history = settings.value(key).toStringList();
+  foreach(QString path, history)
+    {
+    d->ComboBox->addItem(path);
+    if (d->ComboBox->count() >= ctkPathLineEditPrivate::sMaxHistory)
+      {
+      break;
+      }
+    }
+  //select the most recent file location
+  if (this->currentPath().isEmpty())
+    {
+    d->ComboBox->setCurrentIndex(0);
+    }
+}
+
+//-----------------------------------------------------------------------------
+void ctkPathLineEdit::addCurrentPathToHistory()
+{
+  Q_D(ctkPathLineEdit);
+  if (this->currentPath().isEmpty())
+    {
+    return;
+    }
+  QSettings settings;
+  //keep the same values, add the current value
+  //if more than m_MaxHistory entrees, drop the oldest.
+  QString key = "ctkPathLineEdit/" + this->objectName();
+  QStringList history = settings.value(key).toStringList();
+  if (history.contains(this->currentPath()))
+    {
+    history.removeAll(this->currentPath());
+    }
+  history.push_front(this->currentPath());
+  settings.setValue(key, history);
+  int index =d->ComboBox->findText(this->currentPath());
+  if (index >= 0)
+    {
+    d->ComboBox->removeItem(index);
+    }
+  while (d->ComboBox->count() >= ctkPathLineEditPrivate::sMaxHistory)
+    {
+    d->ComboBox->removeItem(d->ComboBox->count() - 1);
+    }
+  d->ComboBox->insertItem(0, this->currentPath());
+}
+
+//------------------------------------------------------------------------------
+void ctkPathLineEdit::setCurrentFileExtension(const QString& extension)
+{
+  QString filename = this->currentPath();
+  QFileInfo fileInfo(filename);
+
+  if (!fileInfo.suffix().isEmpty())
+    {
+    filename.replace(fileInfo.suffix(), extension);
+    }
+  else
+    {
+    filename.append(QString(".") + extension);
+    }
+  this->setCurrentPath(filename);
+}
+
+//------------------------------------------------------------------------------
+QString ctkPathLineEdit::currentPath()const
+{
+  Q_D(const ctkPathLineEdit);
+  return d->ComboBox->currentText();
+}
+
+//------------------------------------------------------------------------------
+void ctkPathLineEdit::setCurrentPath(const QString& path)
+{
+  Q_D(ctkPathLineEdit);
+  d->ComboBox->setEditText(path);
+}
+
+//------------------------------------------------------------------------------
+void ctkPathLineEdit::setCurrentDirectory(const QString& directory)
+{
+  ctkPathLineEditPrivate::sCurrentDirectory = directory;
+}
+
+//------------------------------------------------------------------------------
+void ctkPathLineEdit::updateHasValidInput()
+{
+  Q_D(ctkPathLineEdit);
+
+  bool oldHasValidInput = d->HasValidInput;
+  d->HasValidInput = d->ComboBox->lineEdit()->hasAcceptableInput();
+  if (d->HasValidInput)
+    {
+    QFileInfo fileInfo(d->ComboBox->currentText());
+    ctkPathLineEditPrivate::sCurrentDirectory =
+      fileInfo.isFile() ? fileInfo.absolutePath() : fileInfo.absoluteFilePath();
+    emit currentPathChanged(d->ComboBox->currentText());
+    }
+  if ( d->HasValidInput != oldHasValidInput)
+    {
+    emit validInputChanged(d->HasValidInput);
+    }
+}

+ 164 - 0
Libs/Widgets/ctkPathLineEdit.h

@@ -0,0 +1,164 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+/*=========================================================================
+
+Program:   Maverick
+Module:    $RCSfile: config.h,v $
+
+Copyright (c) Kitware Inc. 28 Corporate Drive,
+Clifton Park, NY, 12065, USA.
+
+All rights reserved. No part of this software may be reproduced, distributed,
+or modified, in any form or by any means, without permission in writing from
+Kitware Inc.
+
+IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
+EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
+"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
+MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+=========================================================================*/
+
+#ifndef __ctkPathLineEdit_h
+#define __ctkPathLineEdit_h
+
+// Qt includes
+#include <QWidget>
+#include <QDir>
+
+// CTK includes
+#include "ctkWidgetsExport.h"
+class ctkPathLineEditPrivate;
+
+/** /class Advanced line edit to select file or directory
+ *  /brief Widget that remember previous selection (using QSettings and objectName)
+ *         the user to select a file on the disk.
+*/
+class CTK_WIDGETS_EXPORT ctkPathLineEdit: public QWidget
+{
+  Q_OBJECT
+  Q_FLAGS(Filters)
+  Q_PROPERTY ( QString label READ label WRITE setLabel )
+
+  Q_PROPERTY ( QStringList nameFilters READ nameFilters WRITE setNameFilters)
+  Q_PROPERTY ( Filters filters READ filters WRITE setFilters)
+  Q_PROPERTY ( QString currentPath READ currentPath WRITE setCurrentPath USER true )
+public:
+  enum Filter { Dirs        = 0x001,
+                Files       = 0x002,
+                Drives      = 0x004,
+                NoSymLinks  = 0x008,
+                AllEntries  = Dirs | Files | Drives,
+                TypeMask    = 0x00f,
+                Readable    = 0x010,
+                Writable    = 0x020,
+                Executable  = 0x040,
+                PermissionMask    = 0x070,
+                Modified    = 0x080,
+                Hidden      = 0x100,
+                System      = 0x200,
+                AccessMask  = 0x3F0,
+                AllDirs       = 0x400,
+                CaseSensitive = 0x800,
+                NoDotAndDotDot = 0x1000, // ### Qt5 NoDotAndDotDot = NoDot|NoDotDot
+                NoDot         = 0x2000,
+                NoDotDot      = 0x4000,
+                NoFilter = -1
+  };
+  Q_DECLARE_FLAGS(Filters, Filter)
+
+  /** Default constructor
+  */
+  ctkPathLineEdit(QWidget *parent = 0);
+
+  /** Constructor
+   *  /param label        Used in file dialogs
+   *  /param nameFilters  Regular expression (in wildcard mode) used to help the user to complete the line,
+   *                      example: "Images (*.jpg *.gif *.png)"
+   *  /param parent       Parent widget
+  */
+  ctkPathLineEdit( const QString& label,
+                   const QStringList& nameFilters,
+                   Filters filters = ctkPathLineEdit::AllEntries,
+                   QWidget *parent=0 );
+  virtual ~ctkPathLineEdit();
+  QString currentPath()const;
+
+  void setLabel(const QString &label);
+  const QString& label()const;
+
+  void setNameFilters(const QStringList &nameFilters);
+  const QStringList& nameFilters()const;
+
+  void setFilters(const Filters& filters);
+  Filters filters()const;
+
+  /** Change the current extension of the edit line.
+   *  If there is no extension yet, set it
+  */
+  void setCurrentFileExtension(const QString& extension);
+
+signals:
+  /** the signal is emit when the state of hasValidInput changed
+  */
+  void validInputChanged(bool);
+
+  void currentPathChanged(const QString& path);
+
+public slots:
+  void setCurrentPath(const QString& path);
+
+  /** Open a QFileDialog to select a file or directory and set current text to it
+   *  You would probably connect a browse push button like this:
+   *  connect(myPushButton,SIGNAL(clicked()),myPathLineEdit,SLOT(browse()))
+  */
+  void browse();
+
+  /** Load the history of the paths. To be restored the inputs must have been saved by
+   *  saveCurrentPathInHistory().
+  */
+
+  void retrieveHistory();
+  /** Save the current value (this->currentPath()) into the history. That value will be retrieved
+   *  next time the retrieveHistory()
+  */
+  void addCurrentPathToHistory();
+
+protected slots:
+  void setCurrentDirectory(const QString& directory);
+  void updateHasValidInput();
+
+protected:
+  QScopedPointer<ctkPathLineEditPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkPathLineEdit);
+  Q_DISABLE_COPY(ctkPathLineEdit);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(ctkPathLineEdit::Filters)
+
+#endif // __ctkPathLineEdit_h