[rkward/frameworks] /: Also remember last used location in plugin filebrowsers. While at it, implement basic validity checking.

Thomas Friedrichsmeier thomas.friedrichsmeier at ruhr-uni-bochum.de
Sat Nov 28 14:53:19 UTC 2015


Git commit 94aa5b017ab5280c759ee2e9874a1a9878c61fe8 by Thomas Friedrichsmeier.
Committed on 28/11/2015 at 14:53.
Pushed by tfry into branch 'frameworks'.

Also remember last used location in plugin filebrowsers. While at it, implement basic validity checking.

M  +1    -0    ChangeLog
M  +31   -3    rkward/misc/getfilenamewidget.cpp
M  +4    -0    rkward/misc/getfilenamewidget.h
M  +79   -8    rkward/plugin/rkpluginbrowser.cpp
M  +9    -3    rkward/plugin/rkpluginbrowser.h

http://commits.kde.org/rkward/94aa5b017ab5280c759ee2e9874a1a9878c61fe8

diff --git a/ChangeLog b/ChangeLog
index 270b5ec..90bc0f5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,4 @@
+- File selection fields in plugin dialogs remember the last used directory (per session), and check for a valid selection
 - Better handling of text drag-and-drop inside the R console window
 
 --- Version 0.6.4 - XXXXXXXXXXXXXXX
diff --git a/rkward/misc/getfilenamewidget.cpp b/rkward/misc/getfilenamewidget.cpp
index f0b9e71..d23c056 100644
--- a/rkward/misc/getfilenamewidget.cpp
+++ b/rkward/misc/getfilenamewidget.cpp
@@ -24,6 +24,8 @@
 #include <KLineEdit>
 #include <kurlrequester.h>
 
+#include "../settings/rksettingsmodulegeneral.h"
+
 #include "../debug.h"
 
 GetFileNameWidget::GetFileNameWidget (QWidget *parent, FileType mode, bool only_local, const QString &label, const QString &caption, const QString &initial) : QWidget (parent) {
@@ -35,11 +37,9 @@ GetFileNameWidget::GetFileNameWidget (QWidget *parent, FileType mode, bool only_
 	if (!label.isEmpty ()) vbox->addWidget (new QLabel (label, this));
 
 	edit = new KUrlRequester (this);
-	connect (edit, &KUrlRequester::textChanged, this, &GetFileNameWidget::locationEditChanged);
 	vbox->addWidget (edit);
 
-	edit->setUrl (QUrl::fromUserInput (initial, QString (), QUrl::AssumeLocalFile));
-
+	_mode = mode;
 	KFile::Modes mode_flags;
 	if (mode == ExistingDirectory) {
 		mode_flags = KFile::Directory | KFile::ExistingOnly;
@@ -60,6 +60,26 @@ GetFileNameWidget::GetFileNameWidget (QWidget *parent, FileType mode, bool only_
 	if (only_local) mode_flags |= KFile::LocalOnly;
 	edit->setMode (mode_flags);
 
+	QString append = initial;
+	if (initial.startsWith ('<')) {
+		storage_key = initial.section ('>', 0, 0).mid (1);
+		append = initial.section ('>', 1);
+	}
+	QUrl initial_url = RKSettingsModuleGeneral::lastUsedUrlFor (storage_key);  // storage_key == QString () in the default case is intended
+	if (!append.isEmpty ()) {
+		if (initial_url.isLocalFile ()) {
+			initial_url = QUrl::fromUserInput (append, initial_url.toLocalFile (), QUrl::AssumeLocalFile);
+		} else {
+			initial_url.setPath (initial_url.path () + '/' + append);
+		}
+		initial_url = initial_url.adjusted (QUrl::NormalizePathSegments);
+	}
+	if (initial_url.isLocalFile () || !only_local) {
+		edit->setUrl (initial_url);
+	}
+	connect (edit, &KUrlRequester::textChanged, this, &GetFileNameWidget::locationEditChanged);
+	connect (edit, &KUrlRequester::urlSelected, this, &GetFileNameWidget::updateLastUsedUrl);
+
 	if (caption.isEmpty ()) edit->setWindowTitle (label);
 	else edit->setWindowTitle (caption);
 }
@@ -75,6 +95,14 @@ void GetFileNameWidget::setFilter (const QString &filter) {
 	edit->setFilter (filter);
 }
 
+void GetFileNameWidget::updateLastUsedUrl (const QUrl& url) {
+	RK_TRACE (MISC);
+
+	if (!url.isValid ()) return;
+	if (edit->mode () & KFile::Directory) RKSettingsModuleGeneral::updateLastUsedUrl (storage_key, url);
+	else RKSettingsModuleGeneral::updateLastUsedUrl (storage_key, url.adjusted (QUrl::RemoveFilename));
+}
+
 void GetFileNameWidget::setLocation (const QString &new_location) {
 	RK_TRACE (MISC);
 
diff --git a/rkward/misc/getfilenamewidget.h b/rkward/misc/getfilenamewidget.h
index 64cf7d6..ecbf136 100644
--- a/rkward/misc/getfilenamewidget.h
+++ b/rkward/misc/getfilenamewidget.h
@@ -34,6 +34,7 @@ public:
 
 	GetFileNameWidget (QWidget *parent, FileType mode, bool only_local, const QString &label, const QString &caption, const QString &initial);
 	~GetFileNameWidget ();
+	FileType getMode () const { return (_mode); };
 
 /** set filename pattern filter, e.g. "*.cpp *.cc *.C|C++ Source Files\n*.h *.H|Header files" */
 	void setFilter (const QString &filter);
@@ -46,6 +47,7 @@ public:
 	QString getLocation ();
 public slots:
 	void locationEditChanged (const QString &);
+	void updateLastUsedUrl (const QUrl& url);
 signals:
 	void locationChanged ();
 #ifdef Q_OS_WIN
@@ -53,6 +55,8 @@ private slots:
 	void hackOverrideDirDialog ();
 #endif
 private:
+	FileType _mode;
+	QString storage_key;
 	KUrlRequester *edit;
 };
 
diff --git a/rkward/plugin/rkpluginbrowser.cpp b/rkward/plugin/rkpluginbrowser.cpp
index cc7e190..8c4e438 100644
--- a/rkward/plugin/rkpluginbrowser.cpp
+++ b/rkward/plugin/rkpluginbrowser.cpp
@@ -18,6 +18,8 @@
 #include "rkpluginbrowser.h"
 
 #include <QVBoxLayout>
+#include <QUrl>
+#include <QDir>
 
 #include <klocale.h>
 
@@ -37,7 +39,7 @@ RKPluginBrowser::RKPluginBrowser (const QDomElement &element, RKComponent *paren
 	connect (selection, &RKComponentPropertyBase::valueChanged, this, &RKPluginBrowser::textChanged);
 
 	setRequired (xml->getBoolAttribute (element, "required", true, DL_INFO));
-	connect (requirednessProperty (), &RKComponentPropertyBase::valueChanged, this, &RKPluginBrowser::updateColor);
+	connect (requirednessProperty (), &RKComponentPropertyBase::valueChanged, this, &RKPluginBrowser::validateInput);
 
 	QVBoxLayout *vbox = new QVBoxLayout (this);
 	vbox->setContentsMargins (0, 0, 0, 0);
@@ -48,19 +50,22 @@ RKPluginBrowser::RKPluginBrowser (const QDomElement &element, RKComponent *paren
 	else if (intmode == 1) mode = GetFileNameWidget::ExistingDirectory;
 	else mode = GetFileNameWidget::SaveFile;
 
-	bool only_local = !xml->getBoolAttribute (element, "allow_urls", false, DL_INFO);
+	only_local = !xml->getBoolAttribute (element, "allow_urls", false, DL_INFO);
 
 	label_string = xml->i18nStringAttribute (element, "label", i18n ("Enter filename"), DL_INFO);
 	selector = new GetFileNameWidget (this, mode, only_local, label_string, i18n ("Select"), xml->getStringAttribute (element, "initial", QString (), DL_INFO));
 	QString filter = xml->getStringAttribute (element, "filter", QString (), DL_INFO);
 	if (!filter.isEmpty ()) {
-		filter.append ("\n*|All files");
+		filter.append ("\nAll files (*)");
 		selector->setFilter (filter);
 	}
 	connect (selector, &GetFileNameWidget::locationChanged, this, &RKPluginBrowser::textChangedFromUi);
 
 	vbox->addWidget (selector);
 
+	validation_timer.setSingleShot (true);
+	connect (&validation_timer, &QTimer::timeout, this, &RKPluginBrowser::validateInput);
+
 	// initialize
 	updating = false;
 	textChangedFromUi ();
@@ -76,11 +81,22 @@ void RKPluginBrowser::textChanged (RKComponentPropertyBase *) {
 	if (updating) return;
 	updating = true;
 
-	selector->setLocation (fetchStringValue (selection));
+	if (status != RKComponentBase::Processing) {
+		status = RKComponentBase::Processing;
+		changed ();
+	}
+	validation_timer.start (300);
+
+	QUrl url = QUrl::fromUserInput (selection->value ().toString (), QDir::currentPath (), QUrl::AssumeLocalFile);
+	if (!url.isValid ()) url = QUrl (selector->getLocation ());
+	if (url.url () != selection->value ().toString ()) {
+		// NOTE: We refuse to accept relative urls
+		selection->setValue (url.url ());
+	}
+	selector->setLocation (url.url ());
 	updateColor ();
 
 	updating = false;
-	changed ();
 }
 
 void RKPluginBrowser::textChangedFromUi () {
@@ -89,16 +105,71 @@ void RKPluginBrowser::textChangedFromUi () {
 	selection->setValue (selector->getLocation ());
 }
 
-bool RKPluginBrowser::isValid () {
-	return (!(fetchStringValue (selection).isEmpty ()));
+void RKPluginBrowser::validateInput () {
+	RK_TRACE (PLUGIN);
+
+	QString tip;
+	QUrl url = QUrl::fromUserInput (selection->value ().toString (), QDir::currentPath (), QUrl::AssumeLocalFile);
+	RK_ASSERT (!url.isRelative ());
+	if (url.isValid ()) {
+		if (url.isLocalFile ()) {
+			GetFileNameWidget::FileType mode = selector->getMode ();
+			QFileInfo fi (url.toLocalFile ());
+			if (mode == GetFileNameWidget::ExistingFile || mode == GetFileNameWidget::ExistingDirectory) {
+				if (!fi.exists ()) {
+					tip = i18n ("The file or directory does not exist.");
+					status = RKComponentBase::Unsatisfied;
+				} else if (mode == GetFileNameWidget::ExistingFile && !fi.isFile ()) {
+					tip = i18n ("Only files (not directories) are acceptable, here.");
+					status = RKComponentBase::Unsatisfied;
+				} else if (mode == GetFileNameWidget::ExistingDirectory && !fi.isDir ()) {
+					tip = i18n ("Only directories (not files) are acceptable, here.");
+					status = RKComponentBase::Unsatisfied;
+				} else {
+					status = RKComponentBase::Satisfied;
+				}
+			} else {
+				RK_ASSERT (mode == GetFileNameWidget::SaveFile);
+				if (!(fi.isWritable () || (!fi.exists () && QFileInfo (fi.dir ().absolutePath ()).isWritable ()))) {
+					tip = i18n ("The specified file is not writable.");
+					status = RKComponentBase::Unsatisfied;
+				} else if (fi.isDir ()) {
+					tip = i18n ("You have to specify a filename (not directory) to write to.");
+					status = RKComponentBase::Unsatisfied;
+				} else if (fi.exists ()) {
+					// TODO: soft warning (icon)
+					tip = i18n ("<b>Note:</b> The given file already exists, and will be modified / overwritten.");
+					status = RKComponentBase::Satisfied;
+				} else {
+					status = RKComponentBase::Satisfied;
+				}
+			}
+		} else {
+			if (only_local) {
+				tip = i18n ("Only local files are allowed, here.");
+				status = RKComponentBase::Unsatisfied;
+			} else {
+				tip = i18n ("This url looks valid.");
+				status = RKComponentBase::Satisfied;
+			}
+		}
+	} else {
+		tip = i18n ("The given filename / url is not valid.");
+		status = RKComponentBase::Unsatisfied;
+	}
+	setToolTip (tip);
+	updateColor ();
+	changed ();
 }
 
 void RKPluginBrowser::updateColor () {
 	RK_TRACE (PLUGIN);
 
 	if (isEnabled ()) {
-		if (isSatisfied ()) {
+		if (status == RKComponentBase::Satisfied) {
 			selector->setBackgroundColor (QColor (255, 255, 255));
+		} else if (status == RKComponentBase::Processing) {
+			selector->setBackgroundColor (QColor (255, 255, 0));
 		} else {
 			selector->setBackgroundColor (QColor (255, 0, 0));
 		}
diff --git a/rkward/plugin/rkpluginbrowser.h b/rkward/plugin/rkpluginbrowser.h
index f62611a..af05835 100644
--- a/rkward/plugin/rkpluginbrowser.h
+++ b/rkward/plugin/rkpluginbrowser.h
@@ -22,6 +22,8 @@
 
 #include "rkcomponentproperties.h"
 
+#include <QTimer>
+
 class GetFileNameWidget;
 class QDomElement;
 
@@ -40,17 +42,21 @@ public:
 	~RKPluginBrowser ();
 
 	RKComponentPropertyBase *selection;
-	QVariant value (const QString &modifier=QString ()) { return (selection->value (modifier)); };
+	QVariant value (const QString &modifier=QString ()) override { return (selection->value (modifier)); };
 	QStringList getUiLabelPair () const;
-	int type () { return ComponentBrowser; };
-	bool isValid ();
+	int type () override { return ComponentBrowser; };
+	ComponentStatus recursiveStatus () override { return status; };
 public slots:
 	void textChangedFromUi ();
 	void textChanged (RKComponentPropertyBase *);
+	void validateInput ();
 private:
 	void updateColor ();
+	QTimer validation_timer;
+	ComponentStatus status;
 	GetFileNameWidget *selector;
 	bool updating;
+	bool only_local;
 	QString label_string;
 };
 



More information about the rkward-tracker mailing list