소스 검색

Merge branch '365-filter-out-read-only-directory'

* 365-filter-out-read-only-directory:
  Extend ctkDirectoryButton to optionally filter out readOnly folders.
Jean-Christophe Fillion-Robin 11 년 전
부모
커밋
0e21d85e7b
2개의 변경된 파일73개의 추가작업 그리고 11개의 파일을 삭제
  1. 59 11
      Libs/Widgets/ctkDirectoryButton.cpp
  2. 14 0
      Libs/Widgets/ctkDirectoryButton.h

+ 59 - 11
Libs/Widgets/ctkDirectoryButton.cpp

@@ -20,8 +20,10 @@
 
 // Qt includes
 #include <QDebug>
+#include <QFileSystemModel>
 #include <QHBoxLayout>
 #include <QPushButton>
+#include <QSortFilterProxyModel>
 #include <QStyle>
 
 // CTK includes
@@ -51,6 +53,7 @@ public:
 #endif
   // TODO expose DisplayAbsolutePath into the API
   bool         DisplayAbsolutePath;
+  QFileDialog::AcceptMode AcceptMode;
 };
 
 //-----------------------------------------------------------------------------
@@ -63,6 +66,7 @@ ctkDirectoryButtonPrivate::ctkDirectoryButtonPrivate(ctkDirectoryButton& object)
   this->DialogOptions = ctkDirectoryButton::ShowDirsOnly;
 #endif
   this->DisplayAbsolutePath = true;
+  this->AcceptMode = QFileDialog::AcceptOpen;
 }
 
 //-----------------------------------------------------------------------------
@@ -229,20 +233,64 @@ const ctkDirectoryButton::Options& ctkDirectoryButton::options()const
 }
 
 //-----------------------------------------------------------------------------
+QFileDialog::AcceptMode ctkDirectoryButton::acceptMode() const
+{
+  Q_D(const ctkDirectoryButton);
+  return d->AcceptMode;
+}
+
+//-----------------------------------------------------------------------------
+void ctkDirectoryButton::setAcceptMode(QFileDialog::AcceptMode mode)
+{
+  Q_D(ctkDirectoryButton);
+  d->AcceptMode = mode;
+}
+
+//-----------------------------------------------------------------------------
 void ctkDirectoryButton::browse()
 {
+  // See https://bugreports.qt-project.org/browse/QTBUG-10244
+  class ExcludeReadOnlyFilterProxyModel : public QSortFilterProxyModel
+  {
+  public:
+    ExcludeReadOnlyFilterProxyModel(QObject *parent):QSortFilterProxyModel(parent)
+    {
+    }
+    virtual bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
+    {
+      QString filePath =
+          this->sourceModel()->data(sourceModel()->index(source_row, 0, source_parent),
+          QFileSystemModel::FilePathRole).toString();
+      return QFileInfo(filePath).isWritable();
+    }
+  };
+
   Q_D(ctkDirectoryButton);
-  QString dir =
-    QFileDialog::getExistingDirectory(
-      this,
-      d->DialogCaption.isEmpty() ? this->toolTip() : d->DialogCaption,
-      d->Directory.path(),
-#ifdef USE_QFILEDIALOG_OPTIONS
-      d->DialogOptions);
-#else
-      QFlags<QFileDialog::Option>(int(d->DialogOptions)));
-#endif
-  // An empty directory means that the user cancelled the dialog.
+  QScopedPointer<QFileDialog> fileDialog(
+          new QFileDialog(this, d->DialogCaption.isEmpty() ? this->toolTip() :
+          d->DialogCaption, d->Directory.path()));
+  #ifdef USE_QFILEDIALOG_OPTIONS
+    fileDialog->setOptions(d->DialogOptions);
+  #else
+    fileDialog->setOptions(QFlags<QFileDialog::Option>(int(d->DialogOptions)));
+  #endif
+    fileDialog->setAcceptMode(d->AcceptMode);
+    fileDialog->setFileMode(QFileDialog::DirectoryOnly);
+
+  if (d->AcceptMode == QFileDialog::AcceptSave)
+    {
+    // Ideally "Choose" button of QFileDialog should be disabled if a read-only folder
+    // is selected and the acceptMode was AcceptSave.
+    // This is captured in https://github.com/commontk/CTK/issues/365
+    fileDialog->setProxyModel(new ExcludeReadOnlyFilterProxyModel(fileDialog.data()));
+    }
+
+  QString dir;
+  if (fileDialog->exec())
+    {
+    dir = fileDialog->selectedFiles().at(0);
+    }
+  // An empty directory means either that the user cancelled the dialog or the selected directory is readonly
   if (dir.isEmpty())
     {
     return;

+ 14 - 0
Libs/Widgets/ctkDirectoryButton.h

@@ -43,6 +43,13 @@ class ctkDirectoryButtonPrivate;
 class CTK_WIDGETS_EXPORT ctkDirectoryButton: public QWidget
 {
   Q_OBJECT
+  /// This property holds the accept mode of the dialog.
+  /// The action mode defines whether the dialog is for opening or saving files.
+  /// By default, this property is set to AcceptOpen.
+  /// If set to QFileDialog::AcceptSave mode, the regular behavior of QFileDialog will be extended
+  /// to prevent user from selecting read-only folder. The caveat is that writable folder existing
+  /// in a readonly one won't be selectable.
+  Q_PROPERTY(QFileDialog::AcceptMode acceptMode READ acceptMode WRITE setAcceptMode)
   Q_PROPERTY(QString directory READ directory WRITE setDirectory NOTIFY directoryChanged USER true)
   /// This property holds the title of the file dialog used to select a new directory
   /// If caption is not set, internally use QWidget::tooltip()
@@ -128,6 +135,12 @@ public:
   const Options& options()const;
 #endif
 
+  /// \sa setAcceptMode QFileDialog::AcceptMode
+  QFileDialog::AcceptMode acceptMode() const;
+
+  /// \sa acceptMode QFileDialog::AcceptMode
+  void setAcceptMode(QFileDialog::AcceptMode mode);
+
 public Q_SLOTS:
   /// browse() opens a pop up where the user can select a new directory for the
   /// button. browse() is automatically called when the button is clicked.
@@ -146,6 +159,7 @@ Q_SIGNALS:
   /// the current directory.
   /// \sa directoryChanged
   void directorySelected(const QString&);
+
 protected:
   QScopedPointer<ctkDirectoryButtonPrivate> d_ptr;