ctkDirectoryButton.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QDebug>
  16. #include <QFileSystemModel>
  17. #include <QHBoxLayout>
  18. #include <QPushButton>
  19. #include <QSortFilterProxyModel>
  20. #include <QStyle>
  21. // CTK includes
  22. #include "ctkDirectoryButton.h"
  23. //-----------------------------------------------------------------------------
  24. class ctkDirectoryButtonPrivate
  25. {
  26. Q_DECLARE_PUBLIC(ctkDirectoryButton);
  27. protected:
  28. ctkDirectoryButton* const q_ptr;
  29. public:
  30. ctkDirectoryButtonPrivate(ctkDirectoryButton& object);
  31. void init();
  32. void updateDisplayText();
  33. QDir Directory;
  34. QPushButton* PushButton;
  35. QString DialogCaption;
  36. QString DisplayText;
  37. #ifdef USE_QFILEDIALOG_OPTIONS
  38. QFileDialog::Options DialogOptions;
  39. #else
  40. ctkDirectoryButton::Options DialogOptions;
  41. #endif
  42. // TODO expose DisplayAbsolutePath into the API
  43. bool DisplayAbsolutePath;
  44. QFileDialog::AcceptMode AcceptMode;
  45. };
  46. //-----------------------------------------------------------------------------
  47. ctkDirectoryButtonPrivate::ctkDirectoryButtonPrivate(ctkDirectoryButton& object)
  48. :q_ptr(&object)
  49. {
  50. #if USE_QFILEDIALOG_OPTIONS
  51. this->DialogOptions = QFileDialog::ShowDirsOnly;
  52. #else
  53. this->DialogOptions = ctkDirectoryButton::ShowDirsOnly;
  54. #endif
  55. this->DisplayAbsolutePath = true;
  56. this->AcceptMode = QFileDialog::AcceptOpen;
  57. }
  58. //-----------------------------------------------------------------------------
  59. void ctkDirectoryButtonPrivate::init()
  60. {
  61. Q_Q(ctkDirectoryButton);
  62. this->PushButton = new QPushButton(q);
  63. QObject::connect(this->PushButton, SIGNAL(clicked()), q, SLOT(browse()));
  64. QHBoxLayout* l = new QHBoxLayout(q);
  65. l->addWidget(this->PushButton);
  66. l->setContentsMargins(0,0,0,0);
  67. q->setLayout(l);
  68. q->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed, QSizePolicy::ButtonBox));
  69. }
  70. //-----------------------------------------------------------------------------
  71. void ctkDirectoryButtonPrivate::updateDisplayText()
  72. {
  73. QString buttonText = this->DisplayText;
  74. if (buttonText.isNull())
  75. {
  76. buttonText = this->DisplayAbsolutePath ?
  77. this->Directory.absolutePath() : this->Directory.path();
  78. }
  79. this->PushButton->setText(buttonText);
  80. }
  81. //-----------------------------------------------------------------------------
  82. ctkDirectoryButton::ctkDirectoryButton(QWidget * parentWidget)
  83. :QWidget(parentWidget)
  84. , d_ptr(new ctkDirectoryButtonPrivate(*this))
  85. {
  86. Q_D(ctkDirectoryButton);
  87. d->init();
  88. d->PushButton->setText(d->DisplayAbsolutePath ? d->Directory.absolutePath() : d->Directory.path());
  89. d->PushButton->setIcon(this->style()->standardIcon(QStyle::SP_DirIcon));
  90. }
  91. //-----------------------------------------------------------------------------
  92. ctkDirectoryButton::ctkDirectoryButton(const QString& dir,
  93. QWidget * parentWidget)
  94. :QWidget(parentWidget)
  95. , d_ptr(new ctkDirectoryButtonPrivate(*this))
  96. {
  97. Q_D(ctkDirectoryButton);
  98. d->init();
  99. d->Directory = QDir(dir);
  100. d->PushButton->setText(d->DisplayAbsolutePath ? d->Directory.absolutePath() : d->Directory.path());
  101. d->PushButton->setIcon(this->style()->standardIcon(QStyle::SP_DirIcon));
  102. }
  103. //-----------------------------------------------------------------------------
  104. ctkDirectoryButton::ctkDirectoryButton(
  105. const QIcon & icon, const QString& dir, QWidget * parentWidget)
  106. :QWidget(parentWidget)
  107. , d_ptr(new ctkDirectoryButtonPrivate(*this))
  108. {
  109. Q_D(ctkDirectoryButton);
  110. d->init();
  111. d->Directory = QDir(dir);
  112. d->PushButton->setText(d->DisplayAbsolutePath ? d->Directory.absolutePath() : d->Directory.path());
  113. d->PushButton->setIcon(icon);
  114. }
  115. //-----------------------------------------------------------------------------
  116. ctkDirectoryButton::~ctkDirectoryButton()
  117. {
  118. }
  119. //-----------------------------------------------------------------------------
  120. void ctkDirectoryButton::setDirectory(const QString& dir)
  121. {
  122. Q_D(ctkDirectoryButton);
  123. QDir newDirectory(dir);
  124. if (d->Directory == newDirectory)
  125. {
  126. emit directorySelected(d->DisplayAbsolutePath ?
  127. newDirectory.absolutePath() :
  128. newDirectory.path());
  129. return;
  130. }
  131. d->Directory = newDirectory;
  132. d->updateDisplayText();
  133. emit directorySelected(d->DisplayAbsolutePath ?
  134. newDirectory.absolutePath() :
  135. newDirectory.path());
  136. emit directoryChanged(d->DisplayAbsolutePath ? d->Directory.absolutePath() : d->Directory.path());
  137. }
  138. //-----------------------------------------------------------------------------
  139. QString ctkDirectoryButton::directory()const
  140. {
  141. Q_D(const ctkDirectoryButton);
  142. return d->Directory.path();
  143. }
  144. //-----------------------------------------------------------------------------
  145. void ctkDirectoryButton::setCaption(const QString& caption)
  146. {
  147. Q_D(ctkDirectoryButton);
  148. d->DialogCaption = caption;
  149. }
  150. //-----------------------------------------------------------------------------
  151. const QString& ctkDirectoryButton::caption()const
  152. {
  153. Q_D(const ctkDirectoryButton);
  154. return d->DialogCaption;
  155. }
  156. //-----------------------------------------------------------------------------
  157. void ctkDirectoryButton::setText(const QString& text)
  158. {
  159. Q_D(ctkDirectoryButton);
  160. d->DisplayText = text;
  161. d->updateDisplayText();
  162. }
  163. //-----------------------------------------------------------------------------
  164. const QString& ctkDirectoryButton::text()const
  165. {
  166. Q_D(const ctkDirectoryButton);
  167. return d->DisplayText;
  168. }
  169. //-----------------------------------------------------------------------------
  170. void ctkDirectoryButton::setIcon(const QIcon& newIcon)
  171. {
  172. Q_D(const ctkDirectoryButton);
  173. return d->PushButton->setIcon(newIcon);
  174. }
  175. //-----------------------------------------------------------------------------
  176. QIcon ctkDirectoryButton::icon()const
  177. {
  178. Q_D(const ctkDirectoryButton);
  179. return d->PushButton->icon();
  180. }
  181. //-----------------------------------------------------------------------------
  182. #ifdef USE_QFILEDIALOG_OPTIONS
  183. void ctkDirectoryButton::setOptions(const QFileDialog::Options& dialogOptions)
  184. #else
  185. void ctkDirectoryButton::setOptions(const Options& dialogOptions)
  186. #endif
  187. {
  188. Q_D(ctkDirectoryButton);
  189. d->DialogOptions = dialogOptions;
  190. }
  191. //-----------------------------------------------------------------------------
  192. #ifdef USE_QFILEDIALOG_OPTIONS
  193. const QFileDialog::Options& ctkDirectoryButton::options()const
  194. #else
  195. const ctkDirectoryButton::Options& ctkDirectoryButton::options()const
  196. #endif
  197. {
  198. Q_D(const ctkDirectoryButton);
  199. return d->DialogOptions;
  200. }
  201. //-----------------------------------------------------------------------------
  202. QFileDialog::AcceptMode ctkDirectoryButton::acceptMode() const
  203. {
  204. Q_D(const ctkDirectoryButton);
  205. return d->AcceptMode;
  206. }
  207. //-----------------------------------------------------------------------------
  208. void ctkDirectoryButton::setAcceptMode(QFileDialog::AcceptMode mode)
  209. {
  210. Q_D(ctkDirectoryButton);
  211. d->AcceptMode = mode;
  212. }
  213. //-----------------------------------------------------------------------------
  214. void ctkDirectoryButton::browse()
  215. {
  216. // See https://bugreports.qt-project.org/browse/QTBUG-10244
  217. class ExcludeReadOnlyFilterProxyModel : public QSortFilterProxyModel
  218. {
  219. public:
  220. ExcludeReadOnlyFilterProxyModel(QObject *parent):QSortFilterProxyModel(parent)
  221. {
  222. }
  223. virtual bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
  224. {
  225. QString filePath =
  226. this->sourceModel()->data(sourceModel()->index(source_row, 0, source_parent),
  227. QFileSystemModel::FilePathRole).toString();
  228. return QFileInfo(filePath).isWritable();
  229. }
  230. };
  231. Q_D(ctkDirectoryButton);
  232. QScopedPointer<QFileDialog> fileDialog(
  233. new QFileDialog(this, d->DialogCaption.isEmpty() ? this->toolTip() :
  234. d->DialogCaption, d->Directory.path()));
  235. #ifdef USE_QFILEDIALOG_OPTIONS
  236. fileDialog->setOptions(d->DialogOptions);
  237. #else
  238. fileDialog->setOptions(QFlags<QFileDialog::Option>(int(d->DialogOptions)));
  239. #endif
  240. fileDialog->setAcceptMode(d->AcceptMode);
  241. fileDialog->setFileMode(QFileDialog::DirectoryOnly);
  242. if (d->AcceptMode == QFileDialog::AcceptSave)
  243. {
  244. // Ideally "Choose" button of QFileDialog should be disabled if a read-only folder
  245. // is selected and the acceptMode was AcceptSave.
  246. // This is captured in https://github.com/commontk/CTK/issues/365
  247. fileDialog->setProxyModel(new ExcludeReadOnlyFilterProxyModel(fileDialog.data()));
  248. }
  249. QString dir;
  250. if (fileDialog->exec())
  251. {
  252. dir = fileDialog->selectedFiles().at(0);
  253. }
  254. // An empty directory means either that the user cancelled the dialog or the selected directory is readonly
  255. if (dir.isEmpty())
  256. {
  257. return;
  258. }
  259. this->setDirectory(dir);
  260. }