[rkward-cvs] SF.net SVN: rkward:[2859] trunk/rkward
tfry at users.sourceforge.net
tfry at users.sourceforge.net
Wed May 5 10:00:22 UTC 2010
Revision: 2859
http://rkward.svn.sourceforge.net/rkward/?rev=2859&view=rev
Author: tfry
Date: 2010-05-05 10:00:21 +0000 (Wed, 05 May 2010)
Log Message:
-----------
Add autosave feature for script files
Modified Paths:
--------------
trunk/rkward/ChangeLog
trunk/rkward/rkward/misc/CMakeLists.txt
trunk/rkward/rkward/settings/rksettingsmodulecommandeditor.cpp
trunk/rkward/rkward/settings/rksettingsmodulecommandeditor.h
trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp
trunk/rkward/rkward/windows/rkcommandeditorwindow.h
Added Paths:
-----------
trunk/rkward/rkward/misc/rkjobsequence.cpp
trunk/rkward/rkward/misc/rkjobsequence.h
Modified: trunk/rkward/ChangeLog
===================================================================
--- trunk/rkward/ChangeLog 2010-05-05 09:55:30 UTC (rev 2858)
+++ trunk/rkward/ChangeLog 2010-05-05 10:00:21 UTC (rev 2859)
@@ -1,5 +1,6 @@
TODO: Do not use SmartInterface for KDE 4.5 and above
+- Add option to autosave script files (enabled by default)
- The tabbar in the main window now shows a context menu with options to close/detach a window
- The tabs in the main window can now be re-ordered by dragging with the mouse (left click if compiled with Qt 4.5 or above, middle click for earlier versions)
- Add alternating row backgrounds in data.frame-editor
Modified: trunk/rkward/rkward/misc/CMakeLists.txt
===================================================================
--- trunk/rkward/rkward/misc/CMakeLists.txt 2010-05-05 09:55:30 UTC (rev 2858)
+++ trunk/rkward/rkward/misc/CMakeLists.txt 2010-05-05 10:00:21 UTC (rev 2859)
@@ -13,6 +13,7 @@
rkprogresscontrol.cpp
rksaveobjectchooser.cpp
rkdummypart.cpp
+ rkjobsequence.cpp
rkspecialactions.cpp
rkstandardicons.cpp
rkstandardactions.cpp
Added: trunk/rkward/rkward/misc/rkjobsequence.cpp
===================================================================
--- trunk/rkward/rkward/misc/rkjobsequence.cpp (rev 0)
+++ trunk/rkward/rkward/misc/rkjobsequence.cpp 2010-05-05 10:00:21 UTC (rev 2859)
@@ -0,0 +1,77 @@
+/***************************************************************************
+ rkjobsequence - description
+ -------------------
+ begin : Tue May 04
+ copyright : (C) 2010 by Thomas Friedrichsmeier
+ email : tfry at users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#include "rkjobsequence.h"
+
+#include "../debug.h"
+
+RKJobSequence::RKJobSequence () : QObject () {
+ RK_TRACE (MISC);
+}
+
+RKJobSequence::~RKJobSequence () {
+ RK_TRACE (MISC);
+}
+
+void RKJobSequence::addJob (KJob* job) {
+ RK_TRACE (MISC);
+
+ outstanding_jobs.append (job);
+ connect (job, SIGNAL (result(KJob*)), this, SLOT(jobDone(KJob*)));
+}
+
+bool RKJobSequence::hadError () const {
+ RK_TRACE (MISC);
+
+ return (!_errors.isEmpty ());
+}
+
+QStringList RKJobSequence::errors () const {
+ RK_TRACE (MISC);
+
+ return (_errors);
+}
+
+void RKJobSequence::start () {
+ RK_TRACE (MISC);
+
+ RK_ASSERT (!outstanding_jobs.isEmpty ());
+ nextJob ();
+}
+
+void RKJobSequence::nextJob () {
+ RK_TRACE (MISC);
+
+ if (outstanding_jobs.isEmpty ()) {
+ emit (finished (this));
+ deleteLater ();
+ return;
+ }
+
+ outstanding_jobs.first ()->start ();
+}
+
+void RKJobSequence::jobDone (KJob* job) {
+ RK_TRACE (MISC);
+
+ outstanding_jobs.removeAll (job);
+ if (job->error ()) {
+ _errors.append (job->errorString ());
+ }
+ nextJob ();
+}
+
+#include "rkjobsequence.moc"
Added: trunk/rkward/rkward/misc/rkjobsequence.h
===================================================================
--- trunk/rkward/rkward/misc/rkjobsequence.h (rev 0)
+++ trunk/rkward/rkward/misc/rkjobsequence.h 2010-05-05 10:00:21 UTC (rev 2859)
@@ -0,0 +1,47 @@
+/***************************************************************************
+ rkjobsequence - description
+ -------------------
+ begin : Tue May 04
+ copyright : (C) 2010 by Thomas Friedrichsmeier
+ email : tfry at users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef RKJOBSEQUENCE_H
+#define RKJOBSEQUENCE_H
+
+#include <QStringList>
+
+#include <kjob.h>
+
+/** Simple class to queue up a sequnce of KJob that will be executed one after the other */
+class RKJobSequence : public QObject {
+ Q_OBJECT
+public:
+ RKJobSequence ();
+ ~RKJobSequence ();
+
+ void addJob (KJob* job);
+ bool hadError () const;
+ QStringList errors () const;
+ void start ();
+private slots:
+ void jobDone (KJob* job);
+signals:
+ void finished (RKJobSequence *seq);
+private:
+ void nextJob ();
+
+ QList<KJob*> outstanding_jobs;
+ QStringList _errors;
+};
+
+#endif
\ No newline at end of file
Modified: trunk/rkward/rkward/settings/rksettingsmodulecommandeditor.cpp
===================================================================
--- trunk/rkward/rkward/settings/rksettingsmodulecommandeditor.cpp 2010-05-05 09:55:30 UTC (rev 2858)
+++ trunk/rkward/rkward/settings/rksettingsmodulecommandeditor.cpp 2010-05-05 10:00:21 UTC (rev 2859)
@@ -2,7 +2,7 @@
rksettingsmodulecommandeditor - description
-------------------
begin : Tue Oct 23 2007
- copyright : (C) 2007 by Thomas Friedrichsmeier
+ copyright : (C) 2007, 2010 by Thomas Friedrichsmeier
email : tfry at users.sourceforge.net
***************************************************************************/
@@ -25,6 +25,7 @@
#include <QVBoxLayout>
#include <QCheckBox>
#include <QGroupBox>
+#include <QLineEdit>
#include "../misc/rkspinbox.h"
#include "../rkglobals.h"
@@ -34,6 +35,10 @@
int RKSettingsModuleCommandEditor::completion_min_chars;
int RKSettingsModuleCommandEditor::completion_timeout;
bool RKSettingsModuleCommandEditor::completion_enabled;
+bool RKSettingsModuleCommandEditor::autosave_enabled;
+bool RKSettingsModuleCommandEditor::autosave_keep;
+int RKSettingsModuleCommandEditor::autosave_interval;
+//QString RKSettingsModuleCommandEditor::autosave_suffix;
RKSettingsModuleCommandEditor::RKSettingsModuleCommandEditor (RKSettings *gui, QWidget *parent) : RKSettingsModule (gui, parent) {
RK_TRACE (SETTINGS);
@@ -45,7 +50,7 @@
completion_enabled_box = new QCheckBox (i18n ("Enable code completion"), group);
completion_enabled_box->setChecked (completion_enabled);
- connect (completion_enabled_box, SIGNAL (stateChanged(int)), this, SLOT (settingChanged(int)));
+ connect (completion_enabled_box, SIGNAL (stateChanged(int)), this, SLOT (settingChanged()));
box_layout->addWidget (completion_enabled_box);
box_layout->addSpacing (RKGlobals::spacingHint ());
@@ -55,7 +60,7 @@
completion_min_chars_box = new RKSpinBox (group);
completion_min_chars_box->setIntMode (1, INT_MAX, completion_min_chars);
completion_min_chars_box->setEnabled (completion_enabled);
- connect (completion_min_chars_box, SIGNAL (valueChanged(int)), this, SLOT (settingChanged(int)));
+ connect (completion_min_chars_box, SIGNAL (valueChanged(int)), this, SLOT (settingChanged()));
box_layout->addWidget (label);
box_layout->addWidget (completion_min_chars_box);
@@ -66,12 +71,42 @@
completion_timeout_box = new RKSpinBox (group);
completion_timeout_box->setIntMode (0, INT_MAX, completion_timeout);
completion_timeout_box->setEnabled (completion_enabled);
- connect (completion_timeout_box, SIGNAL (valueChanged(int)), this, SLOT (settingChanged(int)));
+ connect (completion_timeout_box, SIGNAL (valueChanged(int)), this, SLOT (settingChanged()));
box_layout->addWidget (label);
box_layout->addWidget (completion_timeout_box);
main_vbox->addWidget (group);
+ main_vbox->addSpacing (2 * RKGlobals::spacingHint ());
+
+ group = autosave_enabled_box = new QGroupBox (i18n ("Autosaves"), this);
+ autosave_enabled_box->setCheckable (true);
+ autosave_enabled_box->setChecked (autosave_enabled);
+ connect (autosave_enabled_box, SIGNAL (toggled(bool)), this, SLOT (settingChanged()));
+ box_layout = new QVBoxLayout (group);
+
+ label = new QLabel (i18n ("Autosave interval (minutes)"), group);
+ autosave_interval_box = new RKSpinBox (group);
+ autosave_interval_box->setIntMode (1, INT_MAX, autosave_interval);
+ connect (autosave_interval_box, SIGNAL (valueChanged(int)), this, SLOT (settingChanged()));
+ box_layout->addWidget (label);
+ box_layout->addWidget (autosave_interval_box);
+ box_layout->addSpacing (RKGlobals::spacingHint ());
+
+/* label = new QLabel (i18n ("Filename suffix for autosave files"), group);
+ autosave_suffix_edit = new QLineEdit (autosave_suffix, group);
+ connect (autosave_suffix_edit, SIGNAL (textChanged(const QString&)), this, SLOT (settingChanged()));
+ box_layout->addWidget (label);
+ box_layout->addWidget (autosave_suffix_edit);
+ box_layout->addSpacing (RKGlobals::spacingHint ()); */
+
+ autosave_keep_box = new QCheckBox (i18n ("Keep autosave file after manual save"), group);
+ autosave_keep_box->setChecked (autosave_keep);
+ connect (autosave_keep_box, SIGNAL (stateChanged(int)), this, SLOT (settingChanged()));
+ box_layout->addWidget (autosave_keep_box);
+
+ main_vbox->addWidget (group);
+
main_vbox->addStretch ();
}
@@ -79,7 +114,7 @@
RK_TRACE (SETTINGS);
}
-void RKSettingsModuleCommandEditor::settingChanged (int) {
+void RKSettingsModuleCommandEditor::settingChanged () {
RK_TRACE (SETTINGS);
change ();
@@ -103,6 +138,13 @@
completion_enabled = completion_enabled_box->isChecked ();
completion_min_chars = completion_min_chars_box->intValue ();
completion_timeout = completion_timeout_box->intValue ();
+
+ autosave_enabled = autosave_enabled_box->isChecked ();
+ autosave_keep = autosave_keep_box->isChecked ();
+ autosave_interval = autosave_interval_box->intValue ();
+/* autosave_suffix = autosave_suffix_edit->text ();
+ // prevent user from shooting themselves in the foot
+ if (autosave_suffix.isEmpty ()) autosave_suffix = ".autosave"; */
}
void RKSettingsModuleCommandEditor::save (KConfig *config) {
@@ -117,6 +159,11 @@
cg.writeEntry ("Completion enabled", completion_enabled);
cg.writeEntry ("Completion min chars", completion_min_chars);
cg.writeEntry ("Completion timeout", completion_timeout);
+
+ cg.writeEntry ("Autosave enabled", autosave_enabled);
+ cg.writeEntry ("Autosave keep saves", autosave_keep);
+ cg.writeEntry ("Autosave interval", autosave_interval);
+// cg.writeEntry ("Autosave suffix", autosave_suffix);
}
void RKSettingsModuleCommandEditor::loadSettings (KConfig *config) {
@@ -126,6 +173,11 @@
completion_enabled = cg.readEntry ("Completion enabled", true);
completion_min_chars = cg.readEntry ("Completion min chars", 2);
completion_timeout = cg.readEntry ("Completion timeout", 500);
+
+ autosave_enabled = cg.readEntry ("Autosave enabled", true);
+ autosave_keep = cg.readEntry ("Autosave keep saves", false);
+ autosave_interval = cg.readEntry ("Autosave interval", 5);
+// autosave_suffix = cg.readEntry ("Autosave suffix", ".rkward_autosave");
}
#include "rksettingsmodulecommandeditor.moc"
Modified: trunk/rkward/rkward/settings/rksettingsmodulecommandeditor.h
===================================================================
--- trunk/rkward/rkward/settings/rksettingsmodulecommandeditor.h 2010-05-05 09:55:30 UTC (rev 2858)
+++ trunk/rkward/rkward/settings/rksettingsmodulecommandeditor.h 2010-05-05 10:00:21 UTC (rev 2859)
@@ -2,7 +2,7 @@
rksettingsmodulecommandeditor - description
-------------------
begin : Tue Oct 23 2007
- copyright : (C) 2007 by Thomas Friedrichsmeier
+ copyright : (C) 2007, 2010 by Thomas Friedrichsmeier
email : tfry at users.sourceforge.net
***************************************************************************/
@@ -21,6 +21,8 @@
class RKSpinBox;
class QCheckBox;
+class QLineEdit;
+class QGroupBox;
/**
configuration for the Command Editor windows
@@ -47,8 +49,13 @@
static int completionMinChars () { return completion_min_chars; };
static int completionTimeout () { return completion_timeout; };
static bool completionEnabled () { return completion_enabled; };
+
+ static bool autosaveEnabled () { return autosave_enabled; };
+ static bool autosaveKeep () { return autosave_keep; };
+ static int autosaveInterval () { return autosave_interval; };
+ static QString autosaveSuffix () { return ".rkward_autosave"; };
public slots:
- void settingChanged (int);
+ void settingChanged ();
private:
static int completion_min_chars;
static int completion_timeout;
@@ -57,6 +64,16 @@
RKSpinBox* completion_min_chars_box;
RKSpinBox* completion_timeout_box;
QCheckBox* completion_enabled_box;
+
+ static bool autosave_enabled;
+ static bool autosave_keep;
+ static int autosave_interval;
+// static QString autosave_suffix;
+
+ QGroupBox* autosave_enabled_box;
+ QCheckBox* autosave_keep_box;
+ RKSpinBox* autosave_interval_box;
+// QLineEdit* autosave_suffix_edit;
};
#endif
Modified: trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp
===================================================================
--- trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp 2010-05-05 09:55:30 UTC (rev 2858)
+++ trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp 2010-05-05 10:00:21 UTC (rev 2859)
@@ -46,11 +46,15 @@
#include <klibloader.h>
#include <kactioncollection.h>
#include <kactionmenu.h>
+#include <ktemporaryfile.h>
+#include <kio/deletejob.h>
+#include <kio/job.h>
#include "../misc/rkcommonfunctions.h"
#include "../misc/rkstandardicons.h"
#include "../misc/rkstandardactions.h"
#include "../misc/rkxmlguisyncer.h"
+#include "../misc/rkjobsequence.h"
#include "../core/robjectlist.h"
#include "../settings/rksettings.h"
#include "../settings/rksettingsmodulecommandeditor.h"
@@ -108,8 +112,10 @@
layout->addWidget(m_view);
connect (m_doc, SIGNAL (documentUrlChanged (KTextEditor::Document*)), this, SLOT (updateCaption (KTextEditor::Document*)));
- connect (m_doc, SIGNAL (modifiedChanged (KTextEditor::Document*)), this, SLOT (updateCaption (KTextEditor::Document*))); // of course most of the time this causes a redundant call to updateCaption. Not if a modification is undone, however.
+ connect (m_doc, SIGNAL (modifiedChanged (KTextEditor::Document*)), this, SLOT (updateCaption(KTextEditor::Document*))); // of course most of the time this causes a redundant call to updateCaption. Not if a modification is undone, however.
+ connect (m_doc, SIGNAL (modifiedChanged (KTextEditor::Document*)), this, SLOT (autoSaveHandlerModifiedChanged()));
connect (m_doc, SIGNAL (textChanged (KTextEditor::Document*)), this, SLOT (tryCompletionProxy (KTextEditor::Document*)));
+ connect (m_doc, SIGNAL (textChanged (KTextEditor::Document*)), this, SLOT (autoSaveHandlerTextChanged()));
connect (m_view, SIGNAL (selectionChanged(KTextEditor::View*)), this, SLOT (selectionChanged(KTextEditor::View*)));
// somehow the katepart loses the context menu each time it loses focus
connect (m_view, SIGNAL (focusIn(KTextEditor::View*)), this, SLOT (focusIn(KTextEditor::View*)));
@@ -136,6 +142,9 @@
initBlocks ();
RK_ASSERT (smart_iface);
+ autosave_timer = new QTimer (this);
+ connect (autosave_timer, SIGNAL (timeout()), this, SLOT (doAutoSave()));
+
updateCaption (); // initialize
QTimer::singleShot (0, this, SLOT (setPopupMenu ()));
}
@@ -332,6 +341,83 @@
return false;
}
+void RKCommandEditorWindow::autoSaveHandlerModifiedChanged () {
+ RK_TRACE (COMMANDEDITOR);
+
+ if (!isModified ()) {
+ autosave_timer->stop ();
+
+ if (RKSettingsModuleCommandEditor::autosaveKeep ()) return;
+ if (!previous_autosave_url.isValid ()) return;
+ RKJobSequence* dummy = new RKJobSequence ();
+ dummy->addJob (KIO::del (previous_autosave_url));
+ connect (dummy, SIGNAL (finished(RKJobSequence*)), this, SLOT (autoSaveHandlerJobFinished(RKJobSequence*)));
+ dummy->start ();
+ previous_autosave_url.clear ();
+ }
+}
+
+void RKCommandEditorWindow::autoSaveHandlerTextChanged () {
+ RK_TRACE (COMMANDEDITOR);
+
+ if (!isModified ()) return; // may happen after load or undo
+ if (!RKSettingsModuleCommandEditor::autosaveEnabled ()) return;
+ if (!autosave_timer->isActive ()) {
+ autosave_timer->start (RKSettingsModuleCommandEditor::autosaveInterval () * 60 * 1000);
+ }
+}
+
+void RKCommandEditorWindow::doAutoSave () {
+ RK_TRACE (COMMANDEDITOR);
+ RK_ASSERT (isModified ());
+
+ KTemporaryFile save;
+ save.setSuffix (RKSettingsModuleCommandEditor::autosaveSuffix ());
+ RK_ASSERT (save.open ());
+ QTextStream out (&save);
+ out.setCodec ("UTF-8"); // make sure that all characters can be saved, without nagging the user
+ out << m_doc->text ();
+ save.close ();
+ save.setAutoRemove (false);
+
+ RKJobSequence* alljobs = new RKJobSequence ();
+ connect (alljobs, SIGNAL (finished(RKJobSequence*)), this, SLOT (autoSaveHandlerJobFinished(RKJobSequence*)));
+ // backup the old autosave file in case something goes wrong during pushing the new one
+ KUrl backup_autosave_url;
+ if (previous_autosave_url.isValid ()) {
+ backup_autosave_url = previous_autosave_url;
+ backup_autosave_url.setFileName (backup_autosave_url.fileName () + "~");
+ alljobs->addJob (KIO::file_move (previous_autosave_url, backup_autosave_url, -1, KIO::Overwrite));
+ }
+
+ // push the newly written file
+ if (url ().isValid ()) {
+ KUrl autosave_url = url ();
+ autosave_url.setFileName (autosave_url.fileName () + RKSettingsModuleCommandEditor::autosaveSuffix ());
+ alljobs->addJob (KIO::file_move (KUrl::fromLocalFile (save.fileName ()), autosave_url, -1, KIO::Overwrite));
+ previous_autosave_url = autosave_url;
+ } else { // i.e., the document is still "Untitled"
+ previous_autosave_url = KUrl::fromLocalFile (save.fileName ());
+ }
+
+ // remove the backup
+ if (backup_autosave_url.isValid ()) {
+ alljobs->addJob (KIO::del (backup_autosave_url));
+ }
+ alljobs->start ();
+
+ // do not create any more autosaves until the text is changed, again
+ autosave_timer->stop ();
+}
+
+void RKCommandEditorWindow::autoSaveHandlerJobFinished (RKJobSequence* seq) {
+ RK_TRACE (COMMANDEDITOR);
+
+ if (seq->hadError ()) {
+ KMessageBox::detailedError (this, i18n ("An error occurred during while trying to create an autosave of the script file '%1':", url ().url ()), "- " + seq->errors ().join ("\n- "));
+ }
+}
+
KUrl RKCommandEditorWindow::url () {
// RK_TRACE (COMMANDEDITOR);
return (m_doc->url ());
Modified: trunk/rkward/rkward/windows/rkcommandeditorwindow.h
===================================================================
--- trunk/rkward/rkward/windows/rkcommandeditorwindow.h 2010-05-05 09:55:30 UTC (rev 2858)
+++ trunk/rkward/rkward/windows/rkcommandeditorwindow.h 2010-05-05 10:00:21 UTC (rev 2859)
@@ -116,6 +116,7 @@
};
class QTimer;
+class RKJobSequence;
/**
\brief Provides an editor window for R-commands, as well as a text-editor window in general.
@@ -199,6 +200,14 @@
/** run a block */
void runBlock ();
void clearUnusedBlocks ();
+/** creates an autosave file */
+ void doAutoSave ();
+/** handler to control when autosaves should be created */
+ void autoSaveHandlerModifiedChanged ();
+/** handler to control when autosaves should be created */
+ void autoSaveHandlerTextChanged ();
+/** handle any errors during auto-saving */
+ void autoSaveHandlerJobFinished (RKJobSequence* seq);
private:
KTextEditor::Document *m_doc;
KTextEditor::View *m_view;
@@ -237,6 +246,9 @@
KAction* action_setwd_to_script;
KAction* action_help_function;
+
+ KUrl previous_autosave_url;
+ QTimer* autosave_timer;
};
#endif
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the rkward-tracker
mailing list