Parcourir la source

Add ctkCompleter to add more flexibility to filtering

Julien Finet il y a 13 ans
Parent
commit
9c7124c71b

+ 3 - 0
Libs/Widgets/CMakeLists.txt

@@ -30,6 +30,8 @@ SET(KIT_SRCS
   ctkCheckablePushButton.h
   ctkComboBox.cpp
   ctkComboBox.h
+  ctkCompleter.cpp
+  ctkCompleter.h
   ctkCollapsibleButton.cpp
   ctkCollapsibleButton.h
   ctkCollapsibleGroupBox.cpp
@@ -182,6 +184,7 @@ SET(KIT_MOC_SRCS
   ctkCheckablePushButton.h
   ctkCheckBoxPixmaps.h
   ctkComboBox.h
+  ctkCompleter.h
   ctkCollapsibleButton.h
   ctkCollapsibleGroupBox.h
   ctkColorDialog.h

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

@@ -18,6 +18,7 @@ SET(TEST_SOURCES
   ctkColorDialogTest2.cpp
   ctkColorPickerButtonTest1.cpp
   ctkComboBoxTest1.cpp
+  ctkCompleterTest1.cpp
   ctkConfirmExitDialogTest1.cpp
   ctkConsoleTest1.cpp
   ctkCoordinatesWidgetTest1.cpp
@@ -141,6 +142,7 @@ SIMPLE_TEST( ctkColorDialogTest1 )
 SIMPLE_TEST( ctkColorDialogTest2 )
 SIMPLE_TEST( ctkColorPickerButtonTest1 )
 SIMPLE_TEST( ctkComboBoxTest1 )
+SIMPLE_TEST( ctkCompleterTest1 )
 SIMPLE_TEST( ctkConfirmExitDialogTest1 )
 SIMPLE_TEST( ctkConsoleTest1 )
 SIMPLE_TEST( ctkCoordinatesWidgetTest1 )

+ 61 - 0
Libs/Widgets/Testing/Cpp/ctkCompleterTest1.cpp

@@ -0,0 +1,61 @@
+/*=========================================================================
+
+  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.
+
+=========================================================================*/
+
+// Qt includes
+#include <QApplication>
+#include <QLineEdit>
+#include <QStringListModel>
+#include <QTimer>
+
+// CTK includes
+#include "ctkCompleter.h"
+
+//-----------------------------------------------------------------------------
+int ctkCompleterTest1(int argc, char * argv [] )
+{
+  QApplication app(argc, argv);
+  
+  QStringList list;
+  list << "toto tutu";
+  list << "tutu, tata";
+  list << "titi, tata";
+  list << "tititi";
+  
+  QStringListModel* model = new QStringListModel(list);
+  ctkCompleter* completer = new ctkCompleter(model);
+  completer->setCaseSensitivity(Qt::CaseInsensitive);
+
+  QLineEdit lineEdit;
+  lineEdit.setCompleter(completer);
+
+  completer->setModelFiltering(ctkCompleter::FilterStartsWith);
+  completer->setModelFiltering(ctkCompleter::FilterWordStartsWith);
+  completer->setModelFiltering(ctkCompleter::FilterContains);
+  
+  lineEdit.show();
+
+  if (argc < 2 || QString(argv[1]) != "-I" )
+    {
+    QTimer::singleShot(200, &app, SLOT(quit()));
+    }
+
+  return app.exec();
+}
+

+ 198 - 0
Libs/Widgets/ctkCompleter.cpp

@@ -0,0 +1,198 @@
+/*=========================================================================
+
+  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.
+
+=========================================================================*/
+
+// Qt includes
+#include <QDebug>
+#include <QSortFilterProxyModel>
+#include <QStringList>
+
+// CTK includes
+#include "ctkCompleter.h"
+
+// -------------------------------------------------------------------------
+class ctkCompleterPrivate
+{
+  Q_DECLARE_PUBLIC(ctkCompleter);
+protected:
+  ctkCompleter* const q_ptr;
+
+public:
+  ctkCompleterPrivate(ctkCompleter& object);
+  ~ctkCompleterPrivate();
+  void init();
+  
+  QStringList splitPath(const QString& path);
+  void updateSortFilterProxyModel();
+
+  ctkCompleter::ModelFiltering Filtering;
+  QSortFilterProxyModel* SortFilterProxyModel;
+};
+
+// -------------------------------------------------------------------------
+ctkCompleterPrivate::ctkCompleterPrivate(ctkCompleter& object)
+  :q_ptr(&object)
+{
+  qRegisterMetaType<ctkCompleter::ModelFiltering>("ctkCompleter::ModelFiltering");
+  this->Filtering = ctkCompleter::FilterStartsWith;
+  this->SortFilterProxyModel = 0;
+}
+
+// -------------------------------------------------------------------------
+ctkCompleterPrivate::~ctkCompleterPrivate()
+{
+  delete this->SortFilterProxyModel;
+}
+
+// -------------------------------------------------------------------------
+void ctkCompleterPrivate::init()
+{
+  Q_Q(ctkCompleter);
+  this->SortFilterProxyModel = new QSortFilterProxyModel(0);
+}
+
+// -------------------------------------------------------------------------
+QStringList ctkCompleterPrivate::splitPath(const QString& s)
+{
+  Q_Q(ctkCompleter);
+  switch(q->modelFiltering())
+    {
+    default:
+    case ctkCompleter::FilterStartsWith:
+      return q->QCompleter::splitPath(s);
+      break;
+    case ctkCompleter::FilterContains:
+      this->updateSortFilterProxyModel();
+      this->SortFilterProxyModel->setFilterWildcard(s);
+      return QStringList();
+      break;
+    case ctkCompleter::FilterWordStartsWith:
+      {
+      this->updateSortFilterProxyModel();
+      QRegExp regexp = QRegExp(QRegExp::escape(s));
+      regexp.setCaseSensitivity(q->caseSensitivity());
+      this->SortFilterProxyModel->setFilterRegExp(regexp);
+      return QStringList();
+      break;
+      }
+    }
+  return q->QCompleter::splitPath(s);
+}
+
+// -------------------------------------------------------------------------
+void ctkCompleterPrivate::updateSortFilterProxyModel()
+{
+  Q_Q(ctkCompleter);
+  this->SortFilterProxyModel->setFilterCaseSensitivity(q->caseSensitivity());
+  this->SortFilterProxyModel->setFilterKeyColumn(q->completionColumn());
+}
+
+// -------------------------------------------------------------------------
+ctkCompleter::ctkCompleter(QObject* parent)
+  : QCompleter(parent)
+  , d_ptr(new ctkCompleterPrivate(*this))
+{
+  Q_D(ctkCompleter);
+  d->init();
+}
+
+// -------------------------------------------------------------------------
+ctkCompleter::ctkCompleter(QAbstractItemModel* model, QObject* parent)
+  : QCompleter(model, parent)
+  , d_ptr(new ctkCompleterPrivate(*this))
+{
+  Q_D(ctkCompleter);
+  d->init();
+}
+
+// -------------------------------------------------------------------------
+ctkCompleter::ctkCompleter(const QStringList& list, QObject* parent)
+  : QCompleter(list, parent)
+  , d_ptr(new ctkCompleterPrivate(*this))
+{
+  Q_D(ctkCompleter);
+  d->init();
+}
+
+// -------------------------------------------------------------------------
+ctkCompleter::~ctkCompleter()
+{
+}
+
+// -------------------------------------------------------------------------
+ctkCompleter::ModelFiltering ctkCompleter::modelFiltering() const
+{
+  Q_D(const ctkCompleter);
+  return d->Filtering;
+}
+
+// -------------------------------------------------------------------------
+void ctkCompleter::setModelFiltering(ModelFiltering filter)
+{
+  Q_D(ctkCompleter);
+  if (filter == d->Filtering)
+    {
+    return;
+    }
+  QAbstractItemModel* source = this->sourceModel();
+  d->Filtering = filter;
+  this->setSourceModel(source);
+  Q_ASSERT(this->sourceModel());
+  // Update the filtering
+  this->setCompletionPrefix(this->completionPrefix());
+}
+
+// -------------------------------------------------------------------------
+QStringList ctkCompleter::splitPath(const QString& s)const
+{
+  Q_D(const ctkCompleter);
+  return const_cast<ctkCompleterPrivate*>(d)->splitPath(s);
+}
+
+// -------------------------------------------------------------------------
+QAbstractItemModel* ctkCompleter::sourceModel()const
+{
+  Q_D(const ctkCompleter);
+  if (d->Filtering != ctkCompleter::FilterStartsWith)
+    {
+    return d->SortFilterProxyModel->sourceModel();
+    }
+  return this->QCompleter::model();
+}
+
+// -------------------------------------------------------------------------
+void ctkCompleter::setSourceModel(QAbstractItemModel* source)
+{
+  Q_D(ctkCompleter);
+  QAbstractItemModel* model = source;
+  if (d->Filtering != ctkCompleter::FilterStartsWith)
+    {
+    d->SortFilterProxyModel->setSourceModel(source);
+    if (source && source->parent() == this)
+      {
+      source->setParent(d->SortFilterProxyModel);
+      }
+    model = d->SortFilterProxyModel;
+    }
+  else if (source && source->parent() == d->SortFilterProxyModel)
+    {
+    source->setParent(this);
+    }
+  this->setModel(model);
+}

+ 99 - 0
Libs/Widgets/ctkCompleter.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 __ctkCompleter_h
+#define __ctkCompleter_h
+
+// Qt includes
+#include <QCompleter>
+#include <QMetaType>
+
+// CTK includes
+#include "ctkWidgetsExport.h"
+class ctkCompleterPrivate;
+
+/// ctkCompleter is a QCompleter that allows different way of filtering
+/// the model, not just by filtering strings that start with the
+/// \sa completionPrefix (default behavior).
+/// ctkCompleter is a bit hackish as it reimplements a methods (splitPath)
+/// from QCompleter in a way that is not intended.
+/// Disclaimer, it might not work in all contexts, but seems to work
+/// fine with a QLineEdit.
+/// e.g.:
+/// QStringList model;
+/// model << "toto tata tutu";
+/// model << "tata toto tutu";
+/// ctkCompleter completer(model);
+/// completer.setModelFiltering(ctkCompleter::FilterWordStartsWith);
+/// QLineEdit lineEdit;
+/// lineEdit.setCompleter(&completer);
+/// ...
+/// If the user types "ta", both entries will show up in the completer
+/// If the user types "ot", no entries will show up in the completer
+/// however using \sa FilterContains would have shown both.
+class CTK_WIDGETS_EXPORT ctkCompleter: public QCompleter
+{
+  Q_OBJECT
+  Q_ENUMS(ModelFiltering)
+  /// FilterStartsWith is the default behavior (same as QCompleter).The
+  /// completer filters out strings that don't start with \sa completionPrefix
+  /// FilterContains is the most permissive filter, the completer filters out
+  /// only strings that don't contain the characters from \sa completionPrefix
+  /// FilterWordStartsWith is useful when strings contain space separated words
+  /// and \sa completionPrefix applies to the beginnig of any of the words in the
+  /// string.
+  Q_PROPERTY(ModelFiltering modelFiltering READ modelFiltering WRITE setModelFiltering)
+
+public:
+  ctkCompleter(QObject* parent = 0);
+  ctkCompleter(QAbstractItemModel* model, QObject* parent = 0);
+  ctkCompleter(const QStringList& list, QObject* parent = 0 );
+  virtual ~ctkCompleter();
+
+  enum ModelFiltering
+    {
+    FilterStartsWith=0,
+    FilterContains,
+    FilterWordStartsWith
+    };
+
+  ModelFiltering modelFiltering()const;
+  void setModelFiltering(ModelFiltering filter);
+
+  virtual QStringList splitPath(const QString& s)const;
+
+  /// ctkCompleter::model() might return a filtered model
+  /// (QSortFilterAbstractModel) different from the one that was set.
+  /// QCompleter::setModel should not be used and setSourceModel used
+  /// instead.
+  QAbstractItemModel* sourceModel()const;
+  void setSourceModel(QAbstractItemModel* model);
+
+protected:
+  QScopedPointer<ctkCompleterPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkCompleter);
+  Q_DISABLE_COPY(ctkCompleter);  
+};
+
+Q_DECLARE_METATYPE(ctkCompleter::ModelFiltering)
+
+#endif // __ctkCompleter_h