|
@@ -29,6 +29,8 @@
|
|
|
#include <QRegExp>
|
|
|
#include <QRegExpValidator>
|
|
|
#include <QSettings>
|
|
|
+#include <QStyleOptionComboBox>
|
|
|
+#include <QToolButton>
|
|
|
|
|
|
// CTK includes
|
|
|
#include "ctkPathLineEdit.h"
|
|
@@ -45,9 +47,17 @@ protected:
|
|
|
public:
|
|
|
ctkPathLineEditPrivate(ctkPathLineEdit& object);
|
|
|
void init();
|
|
|
+ QSize sizeHint(const QString& text)const;
|
|
|
void updateFilter();
|
|
|
|
|
|
+ void createPathLineEditWidget(bool useComboBox);
|
|
|
+ QString settingKey()const;
|
|
|
+
|
|
|
+ QLineEdit* LineEdit;
|
|
|
QComboBox* ComboBox;
|
|
|
+ QToolButton* BrowseButton; //!< "..." button
|
|
|
+
|
|
|
+ int MinimumContentsLength;
|
|
|
|
|
|
QString Label; //!< used in file dialogs
|
|
|
QStringList NameFilters; //!< Regular expression (in wildcard mode) used to help the user to complete the line
|
|
@@ -59,6 +69,7 @@ public:
|
|
|
#endif
|
|
|
|
|
|
bool HasValidInput; //!< boolean that stores the old state of valid input
|
|
|
+ QString SettingKey;
|
|
|
|
|
|
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
|
|
@@ -71,7 +82,10 @@ int ctkPathLineEditPrivate::sMaxHistory = 5;
|
|
|
ctkPathLineEditPrivate::ctkPathLineEditPrivate(ctkPathLineEdit& object)
|
|
|
:q_ptr(&object)
|
|
|
{
|
|
|
+ this->LineEdit = 0;
|
|
|
this->ComboBox = 0;
|
|
|
+ this->BrowseButton = 0;
|
|
|
+ this->MinimumContentsLength = 17;
|
|
|
this->HasValidInput = false;
|
|
|
this->Filters = QDir::AllEntries|QDir::NoDotAndDotDot|QDir::Readable;
|
|
|
}
|
|
@@ -80,20 +94,106 @@ ctkPathLineEditPrivate::ctkPathLineEditPrivate(ctkPathLineEdit& object)
|
|
|
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);
|
|
|
+ layout->setSpacing(0); // no space between the combobx and button
|
|
|
+
|
|
|
+ this->createPathLineEditWidget(true);
|
|
|
+
|
|
|
+ this->BrowseButton = new QToolButton(q);
|
|
|
+ this->BrowseButton->setText("...");
|
|
|
+ // Don't vertically stretch the path line edit unnecessary
|
|
|
+ this->BrowseButton->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored));
|
|
|
+ this->BrowseButton->setToolTip(q->tr("Open a dialog"));
|
|
|
+
|
|
|
+ QObject::connect(this->BrowseButton,SIGNAL(clicked()),
|
|
|
+ q, SLOT(browse()));
|
|
|
+
|
|
|
+ layout->addWidget(this->BrowseButton);
|
|
|
|
|
|
- this->ComboBox->setEditable(true);
|
|
|
q->setSizePolicy(QSizePolicy(
|
|
|
QSizePolicy::Expanding, QSizePolicy::Fixed,
|
|
|
QSizePolicy::LineEdit));
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+void ctkPathLineEditPrivate::createPathLineEditWidget(bool useComboBox)
|
|
|
+{
|
|
|
+ Q_Q(ctkPathLineEdit);
|
|
|
+
|
|
|
+ QString path = q->currentPath();
|
|
|
|
|
|
- QObject::connect(this->ComboBox,SIGNAL(editTextChanged(QString)),
|
|
|
+ if (useComboBox)
|
|
|
+ {
|
|
|
+ this->ComboBox = new QComboBox(q);
|
|
|
+ this->ComboBox->setEditable(true);
|
|
|
+ this->ComboBox->setInsertPolicy(QComboBox::NoInsert);
|
|
|
+ this->LineEdit = this->ComboBox->lineEdit();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ this->ComboBox = 0;
|
|
|
+ this->LineEdit = new QLineEdit(q);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (q->layout() && q->layout()->itemAt(0))
|
|
|
+ {
|
|
|
+ delete q->layout()->itemAt(0)->widget();
|
|
|
+ }
|
|
|
+ qobject_cast<QHBoxLayout*>(q->layout())->insertWidget(
|
|
|
+ 0,
|
|
|
+ this->ComboBox ? qobject_cast<QWidget*>(this->ComboBox) :
|
|
|
+ qobject_cast<QWidget*>(this->LineEdit));
|
|
|
+
|
|
|
+ this->updateFilter();
|
|
|
+ q->retrieveHistory();
|
|
|
+ q->setCurrentPath(path);
|
|
|
+
|
|
|
+ QObject::connect(this->LineEdit, SIGNAL(textChanged(QString)),
|
|
|
q, SLOT(setCurrentDirectory(QString)));
|
|
|
- QObject::connect(this->ComboBox,SIGNAL(editTextChanged(QString)),
|
|
|
+ QObject::connect(this->LineEdit, SIGNAL(textChanged(QString)),
|
|
|
q, SLOT(updateHasValidInput()));
|
|
|
+ q->updateGeometry();
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+QSize ctkPathLineEditPrivate::sizeHint(const QString& text)const
|
|
|
+{
|
|
|
+ Q_Q(const ctkPathLineEdit);
|
|
|
+ int frame = 0;
|
|
|
+ if (this->ComboBox)
|
|
|
+ {
|
|
|
+ QStyleOptionComboBox option;
|
|
|
+ int arrowWidth = this->ComboBox->style()->subControlRect(
|
|
|
+ QStyle::CC_ComboBox, &option, QStyle::SC_ComboBoxArrow, this->ComboBox).width()
|
|
|
+ + (this->ComboBox->hasFrame() ? 2 : 0);
|
|
|
+ frame = 2 * (this->ComboBox->hasFrame() ? 3 : 0)
|
|
|
+ + arrowWidth
|
|
|
+ + 1; // for mac style, not sure why
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ QStyleOptionFrame option;
|
|
|
+ int frameWidth = this->LineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &option, q);
|
|
|
+ int horizontalMargin = 2; // QLineEditPrivate::horizontalMargin
|
|
|
+ // See QLineEdit::sizeHint
|
|
|
+ frame = 2 * frameWidth
|
|
|
+ + this->LineEdit->textMargins().left()
|
|
|
+ + this->LineEdit->textMargins().right()
|
|
|
+ + this->LineEdit->contentsMargins().left()
|
|
|
+ + this->LineEdit->contentsMargins().right()
|
|
|
+ + 2 * horizontalMargin;
|
|
|
+ }
|
|
|
+ int browseWidth = 0;
|
|
|
+ if (q->showBrowseButton())
|
|
|
+ {
|
|
|
+ browseWidth = this->BrowseButton->minimumSizeHint().width();
|
|
|
+ }
|
|
|
+ int textWidth = this->LineEdit->fontMetrics().width(text);
|
|
|
+ int height = (this->ComboBox ? this->ComboBox->minimumSizeHint() :
|
|
|
+ this->LineEdit->minimumSizeHint()).height();
|
|
|
+ return QSize(frame + textWidth + browseWidth, height);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
@@ -106,7 +206,20 @@ void ctkPathLineEditPrivate::updateFilter()
|
|
|
ctk::nameFiltersToExtensions(this->NameFilters),
|
|
|
this->Filters | QDir::NoDotAndDotDot | QDir::AllDirs,
|
|
|
QDir::Name|QDir::DirsLast, newCompleter));
|
|
|
- this->ComboBox->setCompleter(newCompleter);
|
|
|
+ this->LineEdit->setCompleter(newCompleter);
|
|
|
+
|
|
|
+ // don't accept invalid path
|
|
|
+ QRegExpValidator* validator = new QRegExpValidator(
|
|
|
+ ctk::nameFiltersToRegExp(this->NameFilters), q);
|
|
|
+ this->LineEdit->setValidator(validator);
|
|
|
+}
|
|
|
+
|
|
|
+//-----------------------------------------------------------------------------
|
|
|
+QString ctkPathLineEditPrivate::settingKey()const
|
|
|
+{
|
|
|
+ Q_Q(const ctkPathLineEdit);
|
|
|
+ return QString("ctkPathLineEdit/") +
|
|
|
+ (this->SettingKey.isEmpty() ? q->objectName() : this->SettingKey);
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
@@ -162,9 +275,6 @@ void ctkPathLineEdit::setNameFilters(const QStringList &nameFilters)
|
|
|
Q_D(ctkPathLineEdit);
|
|
|
d->NameFilters = nameFilters;
|
|
|
d->updateFilter();
|
|
|
- d->ComboBox->lineEdit()->setValidator(
|
|
|
- new QRegExpValidator(
|
|
|
- ctk::nameFiltersToRegExp(d->NameFilters), this));
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
@@ -273,12 +383,18 @@ void ctkPathLineEdit::browse()
|
|
|
void ctkPathLineEdit::retrieveHistory()
|
|
|
{
|
|
|
Q_D(ctkPathLineEdit);
|
|
|
+ if (d->ComboBox == 0)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ QString path = this->currentPath();
|
|
|
+ bool wasBlocking = this->blockSignals(true);
|
|
|
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)
|
|
|
+ QString key = d->settingKey();
|
|
|
+ const QStringList history = settings.value(key).toStringList();
|
|
|
+ foreach(const QString& path, history)
|
|
|
{
|
|
|
d->ComboBox->addItem(path);
|
|
|
if (d->ComboBox->count() >= ctkPathLineEditPrivate::sMaxHistory)
|
|
@@ -286,25 +402,32 @@ void ctkPathLineEdit::retrieveHistory()
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
- //select the most recent file location
|
|
|
- if (this->currentPath().isEmpty())
|
|
|
+ // Restore path or select the most recent file location if none set.
|
|
|
+ if (path.isEmpty())
|
|
|
{
|
|
|
+ this->blockSignals(wasBlocking);
|
|
|
d->ComboBox->setCurrentIndex(0);
|
|
|
}
|
|
|
+ else
|
|
|
+ {
|
|
|
+ this->setCurrentPath(path);
|
|
|
+ this->blockSignals(wasBlocking);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
void ctkPathLineEdit::addCurrentPathToHistory()
|
|
|
{
|
|
|
Q_D(ctkPathLineEdit);
|
|
|
- if (this->currentPath().isEmpty())
|
|
|
+ if (d->ComboBox == 0 ||
|
|
|
+ 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();
|
|
|
+ QString key = d->settingKey();
|
|
|
QStringList history = settings.value(key).toStringList();
|
|
|
if (history.contains(this->currentPath()))
|
|
|
{
|
|
@@ -312,7 +435,7 @@ void ctkPathLineEdit::addCurrentPathToHistory()
|
|
|
}
|
|
|
history.push_front(this->currentPath());
|
|
|
settings.setValue(key, history);
|
|
|
- int index =d->ComboBox->findText(this->currentPath());
|
|
|
+ int index = d->ComboBox->findText(this->currentPath());
|
|
|
if (index >= 0)
|
|
|
{
|
|
|
d->ComboBox->removeItem(index);
|
|
@@ -352,14 +475,14 @@ QComboBox* ctkPathLineEdit::comboBox() const
|
|
|
QString ctkPathLineEdit::currentPath()const
|
|
|
{
|
|
|
Q_D(const ctkPathLineEdit);
|
|
|
- return d->ComboBox->currentText();
|
|
|
+ return d->LineEdit ? d->LineEdit->text() : QString();
|
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
void ctkPathLineEdit::setCurrentPath(const QString& path)
|
|
|
{
|
|
|
Q_D(ctkPathLineEdit);
|
|
|
- d->ComboBox->setEditText(path);
|
|
|
+ d->LineEdit->setText(path);
|
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
@@ -374,16 +497,100 @@ void ctkPathLineEdit::updateHasValidInput()
|
|
|
Q_D(ctkPathLineEdit);
|
|
|
|
|
|
bool oldHasValidInput = d->HasValidInput;
|
|
|
- d->HasValidInput = d->ComboBox->lineEdit()->hasAcceptableInput();
|
|
|
+ d->HasValidInput = d->LineEdit->hasAcceptableInput();
|
|
|
if (d->HasValidInput)
|
|
|
{
|
|
|
- QFileInfo fileInfo(d->ComboBox->currentText());
|
|
|
+ QFileInfo fileInfo(this->currentPath());
|
|
|
ctkPathLineEditPrivate::sCurrentDirectory =
|
|
|
fileInfo.isFile() ? fileInfo.absolutePath() : fileInfo.absoluteFilePath();
|
|
|
- emit currentPathChanged(d->ComboBox->currentText());
|
|
|
+ emit currentPathChanged(this->currentPath());
|
|
|
}
|
|
|
- if ( d->HasValidInput != oldHasValidInput)
|
|
|
+ if (d->HasValidInput != oldHasValidInput)
|
|
|
{
|
|
|
emit validInputChanged(d->HasValidInput);
|
|
|
}
|
|
|
+ this->updateGeometry();
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+QString ctkPathLineEdit::settingKey()const
|
|
|
+{
|
|
|
+ Q_D(const ctkPathLineEdit);
|
|
|
+ return d->SettingKey;
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+void ctkPathLineEdit::setSettingKey(const QString& key)
|
|
|
+{
|
|
|
+ Q_D(ctkPathLineEdit);
|
|
|
+ d->SettingKey = key;
|
|
|
+ this->retrieveHistory();
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+bool ctkPathLineEdit::showBrowseButton()const
|
|
|
+{
|
|
|
+ Q_D(const ctkPathLineEdit);
|
|
|
+ return d->BrowseButton->isVisibleTo(const_cast<ctkPathLineEdit*>(this));
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+void ctkPathLineEdit::setShowBrowseButton(bool visible)
|
|
|
+{
|
|
|
+ Q_D(ctkPathLineEdit);
|
|
|
+ d->BrowseButton->setVisible(visible);
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+bool ctkPathLineEdit::showHistoryButton()const
|
|
|
+{
|
|
|
+ Q_D(const ctkPathLineEdit);
|
|
|
+ return d->ComboBox ? true: false;
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+void ctkPathLineEdit::setShowHistoryButton(bool visible)
|
|
|
+{
|
|
|
+ Q_D(ctkPathLineEdit);
|
|
|
+ d->createPathLineEditWidget(visible);
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+int ctkPathLineEdit::minimumContentsLength()const
|
|
|
+{
|
|
|
+ Q_D(const ctkPathLineEdit);
|
|
|
+ return d->MinimumContentsLength;
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+void ctkPathLineEdit::setMinimumContentsLength(int length)
|
|
|
+{
|
|
|
+ Q_D(ctkPathLineEdit);
|
|
|
+ d->MinimumContentsLength = length;
|
|
|
+ this->updateGeometry();
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+QSize ctkPathLineEdit::minimumSizeHint()const
|
|
|
+{
|
|
|
+ Q_D(const ctkPathLineEdit);
|
|
|
+ QString fileName = QString('/') + QFileInfo(this->currentPath()).fileName();
|
|
|
+ if (fileName.size() < d->MinimumContentsLength)
|
|
|
+ {
|
|
|
+ fileName = QString("x").repeated(d->MinimumContentsLength);
|
|
|
+ }
|
|
|
+ QSize hint = d->sizeHint(fileName);
|
|
|
+ return hint;
|
|
|
+}
|
|
|
+
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+QSize ctkPathLineEdit::sizeHint()const
|
|
|
+{
|
|
|
+ Q_D(const ctkPathLineEdit);
|
|
|
+ QString path = this->currentPath();
|
|
|
+ if (path.size() < d->MinimumContentsLength)
|
|
|
+ {
|
|
|
+ path = QString("x").repeated(d->MinimumContentsLength);
|
|
|
+ }
|
|
|
+ return d->sizeHint(path);
|
|
|
}
|