[rkward-cvs] SF.net SVN: rkward: [2183] branches/KDE4_port/rkward

tfry at users.sourceforge.net tfry at users.sourceforge.net
Wed Nov 7 18:11:03 UTC 2007


Revision: 2183
          http://rkward.svn.sourceforge.net/rkward/?rev=2183&view=rev
Author:   tfry
Date:     2007-11-07 10:11:02 -0800 (Wed, 07 Nov 2007)

Log Message:
-----------
Totally rework object change tracking and edit data allocation mechanisms.
The old data-editor will be totally broken, now, but everything else should still work.

Modified Paths:
--------------
    branches/KDE4_port/rkward/core/rcontainerobject.cpp
    branches/KDE4_port/rkward/core/rcontainerobject.h
    branches/KDE4_port/rkward/core/rkmodificationtracker.cpp
    branches/KDE4_port/rkward/core/rkmodificationtracker.h
    branches/KDE4_port/rkward/core/rkvariable.cpp
    branches/KDE4_port/rkward/core/rkvariable.h
    branches/KDE4_port/rkward/core/robject.cpp
    branches/KDE4_port/rkward/core/robject.h
    branches/KDE4_port/rkward/dataeditor/editformatdialog.cpp
    branches/KDE4_port/rkward/dataeditor/editlabelsdialog.cpp
    branches/KDE4_port/rkward/dataeditor/rkeditor.cpp
    branches/KDE4_port/rkward/dataeditor/rkeditordataframe.cpp
    branches/KDE4_port/rkward/dataeditor/rkvareditmodel.cpp
    branches/KDE4_port/rkward/dataeditor/rkvareditmodel.h
    branches/KDE4_port/rkward/dataeditor/twintable.cpp
    branches/KDE4_port/rkward/plugin/rkcomponentproperties.cpp
    branches/KDE4_port/rkward/plugin/rkcomponentproperties.h
    branches/KDE4_port/rkward/robjectviewer.cpp
    branches/KDE4_port/rkward/robjectviewer.h
    branches/KDE4_port/rkward/windows/rkworkplace.cpp
    branches/KDE4_port/rkward/windows/rkworkplace.h

Modified: branches/KDE4_port/rkward/core/rcontainerobject.cpp
===================================================================
--- branches/KDE4_port/rkward/core/rcontainerobject.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/core/rcontainerobject.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -308,11 +308,13 @@
 	} else {
 		ret = new RKVariable (this, name);
 	}
+	ret->type |= Pending;
 
 	if ((position < 0) || (position > childmap.size ())) position = childmap.size ();
 
 	RKGlobals::tracker ()->addObject (ret, this, position, creator);
-	
+#warning TODO shouldn't we create the object in R, here?
+
 	return ret;
 }
 

Modified: branches/KDE4_port/rkward/core/rcontainerobject.h
===================================================================
--- branches/KDE4_port/rkward/core/rcontainerobject.h	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/core/rcontainerobject.h	2007-11-07 18:11:02 UTC (rev 2183)
@@ -44,6 +44,7 @@
 	bool updateStructure (RData *new_data);
 
 	int numChildren () const;
+// KDE 4: TODO: do we need this? Can't we just return a copy of the objectma?
 	RObject **children () const;
 
 	/** like findObject (), but does not recurse, i.e. only direct children */

Modified: branches/KDE4_port/rkward/core/rkmodificationtracker.cpp
===================================================================
--- branches/KDE4_port/rkward/core/rkmodificationtracker.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/core/rkmodificationtracker.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -21,6 +21,7 @@
 
 #include "../rkglobals.h"
 #include "../dataeditor/rkeditor.h"
+#include "../dataeditor/rkvareditmodel.h"
 #include "rcontainerobject.h"
 #include "robjectlist.h"
 #include "../windows/rkworkplace.h"
@@ -36,6 +37,8 @@
 
 RKModificationTracker::~RKModificationTracker () {
 	RK_TRACE (OBJECTS);
+
+	RK_ASSERT (updates_locked == 0);
 }
 
 void RKModificationTracker::lockUpdates (bool lock) {
@@ -52,7 +55,7 @@
 	RK_TRACE (OBJECTS);
 // TODO: allow more than one editor per object
 // WARNING: This does not work, if a sub-object is being edited!
-	RKEditor *ed = object->objectOpened ();
+	RKEditor *ed = objectEditor (object);
 	RK_ASSERT (!((editor) && (!ed)));
 	RK_ASSERT (!(removed_in_workspace && editor));
 
@@ -94,25 +97,8 @@
 		beginRemoveRows (object_index, object_row, object_row);
 	}
 
-// TODO: allow more than one editor per object
-// WARNING: This does not work, if a sub-object is being edited!
-	RKEditor *ed = object->objectOpened ();
+	if (!updates_locked) sendListenerNotification (RObjectListener::ObjectRemoved, object, 0, 0);
 
-	if (ed) ed->removeObject (object);		// READ: delete ed
-/* What's this? A child of a removed complex object may be edited somewhere, but not the whole object. In this case, the editor has no chance of restoring the object, but it still needs to be closed. We search all editors for the removed object */
-	if (object->isContainer ()) {
-		RKWorkplace::RKWorkplaceObjectList list = RKWorkplace::mainWorkplace ()->getObjectList (RKMDIWindow::DataEditorWindow);
-		for (RKWorkplace::RKWorkplaceObjectList::const_iterator it = list.constBegin (); it != list.constEnd (); ++it) {
-			RKEditor *subed = static_cast<RKEditor *> (*it);
-			RObject *subedobj = subed->getObject ();
-			if (static_cast<RContainerObject *> (object)->isParentOf (subedobj, true)) {
-				subed->removeObject (subedobj);
-			}
-		}
-	}
-
-	if (!updates_locked) emit (objectRemoved (object));
-
 	if (delete_obj) object->remove (removed_in_workspace);
 	else object->getContainer ()->removeChildNoDelete (object);
 
@@ -121,17 +107,11 @@
 
 void RKModificationTracker::renameObject (RObject *object, const QString &new_name) {
 	RK_TRACE (OBJECTS);
-// TODO: allow more than one editor per object
-// TODO: find out, whether new object-name is valid
-	RKEditor *ed = object->objectOpened ();
 
 	object->rename (new_name);
 
-// since we may end up with a different name that originally requested, we propagate the change also to the original editor
-	if (ed) ed->renameObject (object);
-
 	if (!updates_locked) {
-		emit (objectPropertiesChanged (object));
+		sendListenerNotification (RObjectListener::MetaChanged, object, 0, 0);
 
 		QModelIndex object_index = indexFor (object);
 		emit (dataChanged (object_index, object_index));
@@ -148,34 +128,17 @@
 
 	parent->insertChild (object, position);
 
-// TODO: allow more than one editor per object
-	RKEditor *ed = 0;
-	if (object->getContainer ()) ed = object->getContainer ()->objectOpened ();
-	RK_ASSERT (!((editor) && (!ed)));
-	
-	if (ed) {
-		if (ed != editor) {
-			ed->addObject (object);
-		}
-	}
-
 	if (!updates_locked) {
-		emit (objectAdded (object));
+		sendListenerNotification (RObjectListener::ChildAdded, parent, position, 0);
 		endInsertRows ();
 	}
 }
 
 void RKModificationTracker::objectMetaChanged (RObject *object) {
 	RK_TRACE (OBJECTS);
-// TODO: allow more than one editor per object
-	RKEditor *ed = object->objectOpened ();
-	
-	if (ed) {
-		ed->updateObjectMeta (object);
-	}
 
 	if (!updates_locked) {
-		emit (objectPropertiesChanged (object));
+		sendListenerNotification (RObjectListener::MetaChanged, object, 0, 0);
 
 		QModelIndex object_index = indexFor (object);
 		emit (dataChanged (object_index, object_index));
@@ -184,22 +147,69 @@
 
 void RKModificationTracker::objectDataChanged (RObject *object, RObject::ChangeSet *changes) {
 	RK_TRACE (OBJECTS);
-// TODO: allow more than one editor per object
-	RKEditor *ed = object->objectOpened ();
 
-	if (ed) {
-		ed->updateObjectData (object, changes);
-	}
+	if (!updates_locked) {
+		sendListenerNotification (RObjectListener::DataChanged, object, 0, changes);
+		delete changes;
 
-	delete changes;
-
-	if (!updates_locked) {
 		QModelIndex object_index = indexFor (object);
 		emit (dataChanged (object_index, object_index));
 	}
 }
 
+void RKModificationTracker::addObjectListener (RObject* object, RObjectListener* listener) {
+	RK_TRACE (OBJECTS);
 
+	listeners.insert (object, listener);
+#warning: probably we should create and check for an appropriate NotificationType, instead
+	if (listener->listenerType () == RObjectListener::DataModel) object->beginEdit ();
+}
+
+void RKModificationTracker::removeObjectListener (RObject* object, RObjectListener* listener) {
+	RK_TRACE (OBJECTS);
+
+	listeners.remove (object, listener);
+#warning: probably we should create and check for an appropriate NotificationType, instead
+	if (listener->listenerType () == RObjectListener::DataModel) object->endEdit ();
+}
+
+void RKModificationTracker::sendListenerNotification (RObjectListener::NotificationType type, RObject* o, int index, RObject::ChangeSet* changes) {
+	RK_TRACE (OBJECTS);
+
+	QList<RObjectListener*> obj_listeners = listeners.values (o);
+	for (int i = obj_listeners.size () - 1; i >= 0; --i) {
+		RObjectListener* listener = obj_listeners[i];
+		if (!listener->wantsNotificationType (type)) continue;
+
+		if (type == RObjectListener::ObjectRemoved) {
+			listener->objectRemoved (o);
+		} else if (type == RObjectListener::ChildAdded) {
+			listener->childAdded (index, o);
+		} else if (type == RObjectListener::MetaChanged) {
+			listener->objectMetaChanged (o);
+		} else if (type == RObjectListener::DataChanged) {
+			listener->objectDataChanged (o, changes);
+		} else {
+			RK_ASSERT (false);
+		}
+	}
+}
+
+RKEditor* RKModificationTracker::objectEditor (RObject* object) {
+	RK_TRACE (OBJECTS);
+
+	QList<RObjectListener*> obj_listeners = listeners.values (object);
+	for (int i = obj_listeners.size () - 1; i >= 0; --i) {
+		RObjectListener* listener = obj_listeners[i];
+		if (!(listener->listenerType () == RObjectListener::DataModel)) continue;
+
+		RKEditor* ed = static_cast<RKVarEditModel*> (listener)->getEditor ();
+		if (ed) return ed;
+	}
+
+	return 0;
+}
+
 ///////////////// RKObjectListModel ///////////////////////////
 
 RKObjectListModel::RKObjectListModel (QObject *parent) : QAbstractItemModel (parent) {
@@ -315,4 +325,51 @@
 	return (createIndex (row, 0, object));
 }
 
+
+///////////////////// RObjectListener ////////////////////////
+
+RObjectListener::RObjectListener (ListenerType type, int notifications) {
+	RK_TRACE (OBJECTS);
+
+	RObjectListener::type = type;
+	RObjectListener::notifications = notifications;
+	num_watched_objects = 0;
+}
+
+RObjectListener::~RObjectListener () {
+	RK_TRACE (OBJECTS);
+
+	RK_ASSERT (num_watched_objects == 0);
+}
+
+void RObjectListener::objectRemoved (RObject*) {
+	RK_ASSERT (false);
+}
+
+void RObjectListener::childAdded (int, RObject*) {
+	RK_ASSERT (false);
+}
+
+void RObjectListener::objectMetaChanged (RObject*) {
+	RK_ASSERT (false);
+}
+
+void RObjectListener::objectDataChanged (RObject*, const RObject::ChangeSet *) {
+	RK_ASSERT (false);
+}
+
+void RObjectListener::listenForObject (RObject* object) {
+	RK_TRACE (OBJECTS);
+
+	RKGlobals::tracker ()->addObjectListener (object, this);
+	++num_watched_objects;
+}
+
+void RObjectListener::stopListenForObject (RObject* object) {
+	RK_TRACE (OBJECTS);
+
+	RKGlobals::tracker ()->removeObjectListener (object, this);
+	--num_watched_objects;
+}
+
 #include "rkmodificationtracker.moc"

Modified: branches/KDE4_port/rkward/core/rkmodificationtracker.h
===================================================================
--- branches/KDE4_port/rkward/core/rkmodificationtracker.h	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/core/rkmodificationtracker.h	2007-11-07 18:11:02 UTC (rev 2183)
@@ -20,12 +20,54 @@
 #include <qobject.h>
 #include <qstring.h>
 #include <QAbstractItemModel>
+#include <QMultiHash>
 
 #include "robject.h"
 
 class RKEditor;
 class RObject;
+class RKModificationTracker;
 
+/** Base class for classes that need to know when certain objects have been changed in some way. */
+class RObjectListener {
+public:
+	enum NotificationType {
+		ObjectRemoved=1,
+		ChildAdded=2,
+		MetaChanged=4,
+		DataChanged=8
+	};
+	enum ListenerType {
+		DataModel,	/** < listener is an RKVarEditModel */
+		ObjectView,
+		Other
+	};
+
+	ListenerType listenerType () const { return type; };
+	bool wantsNotificationType (NotificationType type) const { return (notifications & type); };
+protected:
+	RObjectListener (ListenerType type, int notifications);
+	virtual ~RObjectListener ();
+
+friend class RKModificationTracker;
+	/** reimplement this, if you are listening for an object with notification type ObjectRemoved. The default implementation does nothing and raises an assert */
+	virtual void objectRemoved (RObject* removed);
+	/** reimplement this, if you are listening for an object with notification type ChildAdded. The default implementation does nothing and raises an assert */
+	virtual void childAdded (int index, RObject* parent);
+	/** reimplement this, if you are listening for an object with notification type MetaChanged. The default implementation does nothing and raises an assert */
+	virtual void objectMetaChanged (RObject* changed);
+	/** reimplement this, if you are listening for an object with notification type DataChanged. The default implementation does nothing and raises an assert */
+	virtual void objectDataChanged (RObject* object, const RObject::ChangeSet *changes);
+
+	void listenForObject (RObject* object);
+	void stopListenForObject (RObject* object);
+	void addNotificationType (NotificationType type) { notifications |= type; };
+private:
+	ListenerType type;
+	int notifications;
+	int num_watched_objects;
+};
+
 /** An item model for the RObjectList . Technically this is the base class for RKModificationTracker. The two could be merged, fully, but this way, it's a little easier to see what belongs where, logically. */
 class RKObjectListModel : public QAbstractItemModel {
 public:
@@ -64,7 +106,6 @@
 @author Thomas Friedrichsmeier
 */
 class RKModificationTracker : public RKObjectListModel {
-Q_OBJECT
 public:
 	RKModificationTracker (QObject *parent);
 
@@ -82,16 +123,18 @@
 	void objectDataChanged (RObject *object, RObject::ChangeSet *changes);
 /** recursive! */
 	void lockUpdates (bool lock);
-signals:
-/** classes which are not RKEditor(s) but need to know, when an object was removed, should connect to this signal */
-	void objectRemoved (RObject *object);
-/** classes which are not RKEditor(s) but need to know, when an object was renamed or otherwise changed its properties, should connect to this signal */
-	void objectPropertiesChanged (RObject *object);
-/** classes which are not RKEditor(s) but need to know, when an object was added, should connect to this signal */
-	void objectAdded (RObject *object);
+/** returns (the first) editor that is currently active for this object, or 0, if there is no editor */
+	RKEditor* objectEditor (RObject* object);
 private:
 	int updates_locked;
+/** relay change notifications to connected listeners. This is not pretty, since the arguments change their meanings depending on the type of notification, but for now this is ok */
+	void sendListenerNotification (RObjectListener::NotificationType type, RObject* o, int index, RObject::ChangeSet* changes);
 
+friend class RObjectListener;
+	void addObjectListener (RObject* object, RObjectListener* listener);
+	void removeObjectListener (RObject* object, RObjectListener* listener);
+	QMultiHash<RObject*, RObjectListener*> listeners;
+
 friend class RContainerObject;
 /** uncondiontally remove the given object. Do *not* call this except from RContainerObject::moveChild() or internally from removeObject(). Call removeObject(), instead. */
 	void internalRemoveObject (RObject *object, bool removed_in_workspace, bool delete_obj);

Modified: branches/KDE4_port/rkward/core/rkvariable.cpp
===================================================================
--- branches/KDE4_port/rkward/core/rkvariable.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/core/rkvariable.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -42,11 +42,14 @@
 RKVariable::RKVariable (RContainerObject *parent, const QString &name) : RObject (parent, name) {
 	RK_TRACE (OBJECTS);
 	type = Variable;
+	data = 0;
 	setDataType (RObject::DataNumeric);
 }
 
 RKVariable::~RKVariable () {
 	RK_TRACE (OBJECTS);
+
+	RK_ASSERT (!data);	// endEdit() should have been called
 }
 
 void RKVariable::setVarType (RObject::RDataType new_type, bool sync) {
@@ -57,34 +60,35 @@
 	}
 
 	// if the variable is currently opened for editing, all values need to be rechecked / resynced
-	if (myData ()) {
-		bool internal_sync = myData ()->immediate_sync;
+	if (data) {
+		bool internal_sync = data->immediate_sync;
 		// quick and dirty approach! TODO: make more efficient
 		QStringList list;
 		for (int i=0; i < getLength (); ++i) {
 			list.append (getText (i));
 		}
 
-		if (myData ()->changes) {	// all pending changes are moot
-			delete myData ()->changes;
-			myData ()->changes = 0;
+		if (data->changes) {	// all pending changes are moot
+			delete data->changes;
+			data->changes = 0;
 		}
 
 		// store what we want to keep of the edit data
-		RKEditor *editor = myData ()->editor;
-		ValueLabels *value_labels = myData ()->value_labels;
-		myData ()->value_labels = 0;	// prevent destruction
-		FormattingOptions *formatting_options = myData ()->formatting_options;
-		myData ()->formatting_options = 0;	// prevent destruction
+		int num_listeners = data->num_listeners;
+		ValueLabels *value_labels = data->value_labels;
+		data->value_labels = 0;	// prevent destruction
+		FormattingOptions *formatting_options = data->formatting_options;
+		data->formatting_options = 0;	// prevent destruction
 
 		// destroy and re-allocate edit data
 		discardEditData ();
 		setDataType (new_type);
-		allocateEditData (editor);
+		allocateEditData ();
 
 		// re-set presistent aspects of the edit data
-		myData ()->value_labels = value_labels;
-		myData ()->formatting_options = formatting_options;
+		data->value_labels = value_labels;
+		data->formatting_options = formatting_options;
+		data->num_listeners = num_listeners;
 
 		// re-set all data
 		setSyncing (false);
@@ -125,14 +129,14 @@
 	if (command->getFlags () == ROBJECT_UDPATE_STRUCTURE_COMMAND) {
 		RObject::rCommandDone (command);
 	} else if (command->getFlags () == GET_DATA_COMMAND) {
-		RK_ASSERT (myData ());
+		RK_ASSERT (data);
 		// prevent resyncing of data
 		setSyncing (false);
 
 		RK_ASSERT (command->getDataType () == RData::StructureVector);
 		RK_ASSERT (command->getDataLength () == 3);
 
-		RData *data = command->getStructureVector ()[0];
+		RData *cdata = command->getStructureVector ()[0];
 		RData *levels = command->getStructureVector ()[1];
 		RData *invalids = command->getStructureVector ()[2];
 
@@ -140,36 +144,36 @@
 		RK_ASSERT (levels->getDataType () == RData::StringVector);
 		unsigned int levels_len = levels->getDataLength ();
 		RK_ASSERT (levels_len >= 1);
-		delete myData ()->value_labels;
-		myData ()->value_labels = new RObject::ValueLabels;
+		delete data->value_labels;
+		data->value_labels = new RObject::ValueLabels;
 		if ((levels_len == 1) && levels->getStringVector ()[0].isEmpty ()) {
 			// no levels
 		} else {
 			for (unsigned int i=0; i < levels_len; ++i) {
-				myData ()->value_labels->insert (QString::number (i+1), levels->getStringVector ()[i]);
+				data->value_labels->insert (QString::number (i+1), levels->getStringVector ()[i]);
 			}
 		}
 
 		// now set the data
-		RK_ASSERT (data->getDataLength () == (unsigned int) getLength ()); // not a problem due to the line below, I'd still like to know if / when this happens.
-		extendToLength (data->getDataLength ());
-		if (data->getDataType () == RData::StringVector) {
-			setCharacter (0, getLength () - 1, data->getStringVector ());
-		} else if (data->getDataType () == RData::RealVector) {
-			setNumeric (0, getLength () - 1, data->getRealVector ());
-		} else if (data->getDataType () == RData::IntVector) {
+		RK_ASSERT (cdata->getDataLength () == (unsigned int) getLength ()); // not a problem due to the line below, I'd still like to know if / when this happens.
+		extendToLength (cdata->getDataLength ());
+		if (cdata->getDataType () == RData::StringVector) {
+			setCharacter (0, getLength () - 1, cdata->getStringVector ());
+		} else if (cdata->getDataType () == RData::RealVector) {
+			setNumeric (0, getLength () - 1, cdata->getRealVector ());
+		} else if (cdata->getDataType () == RData::IntVector) {
 			unsigned int len = getLength ();
 			double *dd = new double[len];
 			for (unsigned int i = 0; i < len; ++i) {
-				if (data->getIntVector ()[i] == INT_MIN) dd[i] = NAN;
-				else dd[i] = (double) data->getIntVector ()[i];
+				if (cdata->getIntVector ()[i] == INT_MIN) dd[i] = NAN;
+				else dd[i] = (double) cdata->getIntVector ()[i];
 			}
 			setNumeric (0, getLength () - 1, dd);
 			delete [] dd;
 		}
 
 		// now set the invalid fields (only if they are still NAs in the R data)
-		myData ()->invalid_fields.clear ();
+		data->invalid_fields.clear ();
 		if (invalids->getDataLength () <= 1) {
 			// no invalids
 		} else {
@@ -179,19 +183,19 @@
 			unsigned int invalids_count = invalids_length / 2;
 			for (unsigned int i=0; i < invalids_count; ++i) {
 				int row = invalids->getStringVector ()[i].toInt () - 1;
-				if (myData ()->cell_states[row] & RKVarEditData::NA) {
+				if (data->cell_states[row] & RKVarEditData::NA) {
 					setText (row, invalids->getStringVector ()[invalids_count + i]);
 				}
 			}
 		}
-		myData ()->formatting_options = parseFormattingOptionsString (getMetaProperty ("format"));
+		data->formatting_options = parseFormattingOptionsString (getMetaProperty ("format"));
 
 		ChangeSet *set = new ChangeSet;
 		set->from_index = 0;
 		set->to_index = getLength ();
 		RKGlobals::tracker ()->objectDataChanged (this, set);
 		RKGlobals::tracker ()->objectMetaChanged (this);
-		myData ()->dirty = false;
+		type -= (type & NeedDataUpdate);
 		setSyncing (true);
 	} else {
 		RK_ASSERT (false);
@@ -211,39 +215,10 @@
 	dimensions[0] = len;
 }
 
-// virtual
-void RKVariable::allocateEditData (RKEditor *editor) {
-	RK_TRACE (OBJECTS);
-
-	// this assert should stay even when more than one editor is allowed per object. After all, the edit-data should only ever be allocated once!
-	RK_ASSERT (!myData ());
-	
-	data = new RKVarEditData;
-	myData ()->editor = editor;
-	myData ()->cell_strings = 0;
-	myData ()->cell_doubles = 0;
-	myData ()->cell_states = 0;
-	myData ()->allocated_length = 0;
-	myData ()->immediate_sync = true;
-	myData ()->changes = 0;
-	myData ()->value_labels = 0;
-	myData ()->formatting_options = 0;
-	myData ()->previously_valid = true;
-	myData ()->invalid_fields.setAutoDelete (true);
-	myData ()->dirty = false;
-	myData ()->pending = false;
-
-	extendToLength (getLength ());
-
-	for (int i = 0; i < getLength (); ++i) {
-		myData ()->cell_states[i] = RKVarEditData::NA;
-	}
-}
-
 bool RKVariable::updateType (RData *new_data) {
 	RK_TRACE (OBJECTS);
 
-	if (myData ()) {
+	if (data) {
 		int old_type = type;
 		bool ret = RObject::updateType (new_data);
 		int new_type = type;
@@ -256,89 +231,126 @@
 }
 
 // virtual
-void RKVariable::initializeEditDataToEmpty () {
+void RKVariable::beginEdit () {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
-	
-	for (int row=0; row < getLength (); ++row) {
-		myData ()->cell_states[row] = RKVarEditData::NA;
+
+	if (!data) {
+		allocateEditData ();
+		if (!isPending ()) updateDataFromR (0);
 	}
+	++(data->num_listeners);
 }
 
-void RKVariable::updateDataFromR (RCommandChain *chain) {
+// virtual
+void RKVariable::endEdit () {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
 
-	RKGlobals::rInterface ()->issueCommand (".rk.get.vector.data (" + getFullName () + ')', RCommand::App | RCommand::Sync | RCommand::GetStructuredData, QString::null, this, GET_DATA_COMMAND, chain);
+	RK_ASSERT (data);
+	RK_ASSERT (data->num_listeners > 0);
+	--(data->num_listeners);
+	if (!data->num_listeners) discardEditData ();
 }
 
-// virtual
+void RKVariable::allocateEditData () {
+	RK_TRACE (OBJECTS);
+
+	// edit data should only be allocated once, even if there are multiple editors
+	RK_ASSERT (!data);
+	
+	data = new RKVarEditData;
+	data->cell_strings = 0;
+	data->cell_doubles = 0;
+	data->cell_states = 0;
+	data->allocated_length = 0;
+	data->immediate_sync = true;
+	data->changes = 0;
+	data->value_labels = 0;
+	data->formatting_options = 0;
+	data->previously_valid = true;
+	data->invalid_fields.setAutoDelete (true);
+	data->num_listeners = 0;
+
+	extendToLength (getLength ());
+
+	for (int i = 0; i < getLength (); ++i) {
+		data->cell_states[i] = RKVarEditData::NA;
+	}
+}
+
 void RKVariable::discardEditData () {
 	RK_TRACE (OBJECTS);
 
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
+	RK_ASSERT (!(data->num_listeners));
 
 	if (getDataType () == RObject::DataCharacter) {
-		delete [] myData ()->cell_strings;
-		RK_ASSERT (myData ()->cell_doubles == 0);
+		delete [] data->cell_strings;
+		RK_ASSERT (data->cell_doubles == 0);
 	} else {
-		delete [] myData ()->cell_doubles;
-		RK_ASSERT (myData ()->cell_strings == 0);
+		delete [] data->cell_doubles;
+		RK_ASSERT (data->cell_strings == 0);
 	}
-	delete [] myData ()->cell_states;
+	delete [] data->cell_states;
 
-	RK_ASSERT (!(myData ()->changes));
-	delete myData ()->value_labels;
-	delete myData ()->formatting_options;
-	delete myData ();
+	RK_ASSERT (!(data->changes));
+	delete data->value_labels;
+	delete data->formatting_options;
+	delete data;
 	data = 0;
 }
 
+void RKVariable::updateDataFromR (RCommandChain *chain) {
+	RK_TRACE (OBJECTS);
+	if (!data) return;
+
+	RKGlobals::rInterface ()->issueCommand (".rk.get.vector.data (" + getFullName () + ')', RCommand::App | RCommand::Sync | RCommand::GetStructuredData, QString::null, this, GET_DATA_COMMAND, chain);
+}
+
 void RKVariable::setSyncing (bool immediate) {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 	
-	myData ()->immediate_sync = immediate;
+	data->immediate_sync = immediate;
 	if (!immediate) {
-		if (!myData ()->changes) {
-			myData ()->changes = new ChangeSet;
-			myData ()->changes->from_index = -1;
-			myData ()->changes->to_index = -1;
+		if (!data->changes) {
+			data->changes = new ChangeSet;
+			data->changes->from_index = -1;
+			data->changes->to_index = -1;
 		}
 	} else {
-		delete myData ()->changes;
-		myData ()->changes = 0;
+		delete data->changes;
+		data->changes = 0;
 	}
 }
 
 void RKVariable::syncDataToR () {
 	RK_TRACE (OBJECTS);
-	if (!(myData ()->changes)) return;
+	if (!(data->changes)) return;
 	
 	// TODO
-	writeData (myData ()->changes->from_index, myData ()->changes->to_index);
-	myData ()->changes->from_index = -1;
-	myData ()->changes->to_index = -1;
+	writeData (data->changes->from_index, data->changes->to_index);
+	data->changes->from_index = -1;
+	data->changes->to_index = -1;
 }
 
 void RKVariable::restore (RCommandChain *chain) {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 
 	writeData (0, getLength () - 1, chain);
-	delete myData ()->changes;
+	delete data->changes;
 	writeMetaData (chain);
 }
 
 void RKVariable::writeInvalidField (int row, RCommandChain *chain) {
 	RK_TRACE (OBJECTS);
 
-	if (myData ()->invalid_fields[row]) {
-		RKGlobals::rInterface ()->issueCommand (".rk.set.invalid.field (" + getFullName () + ", " + QString::number (row+1) + ", " + rQuote (*(myData ()->invalid_fields[row])) + ')', RCommand::App | RCommand::Sync, QString::null, 0,0, chain);
+	if (data->invalid_fields[row]) {
+		RKGlobals::rInterface ()->issueCommand (".rk.set.invalid.field (" + getFullName () + ", " + QString::number (row+1) + ", " + rQuote (*(data->invalid_fields[row])) + ')', RCommand::App | RCommand::Sync, QString::null, 0,0, chain);
 	} else {
 		RKGlobals::rInterface ()->issueCommand (".rk.set.invalid.field (" + getFullName () + ", " + QString::number (row+1) + ", NULL)", RCommand::App | RCommand::Sync, QString::null, 0,0, chain);
 	}
-	myData ()->cell_states[row] -= (myData ()->cell_states[row] & RKVarEditData::UnsyncedInvalidState);
+	data->cell_states[row] -= (data->cell_states[row] & RKVarEditData::UnsyncedInvalidState);
 }
 
 void RKVariable::writeData (int from_row, int to_row, RCommandChain *chain) {
@@ -348,7 +360,7 @@
 	// TODO: try to sync in correct storage mode
 	if (from_row == to_row) {
 		RKGlobals::rInterface ()->issueCommand (getFullName () + '[' + QString::number (from_row+1) + "] <- " + getRText (from_row), RCommand::App | RCommand::Sync, QString::null, 0,0, chain);
-		if (myData ()->cell_states[from_row] & RKVarEditData::UnsyncedInvalidState) writeInvalidField (from_row, chain);
+		if (data->cell_states[from_row] & RKVarEditData::UnsyncedInvalidState) writeInvalidField (from_row, chain);
 	} else {
 		QString data_string = "c (";
 		for (int row = from_row; row <= to_row; ++row) {
@@ -357,7 +369,7 @@
 			if (row != to_row) {
 				data_string.append (", ");
 			}
-			if (myData ()->cell_states[row] & RKVarEditData::UnsyncedInvalidState) writeInvalidField (row, chain);
+			if (data->cell_states[row] & RKVarEditData::UnsyncedInvalidState) writeInvalidField (row, chain);
 		}
 		data_string.append (")");
 		RKGlobals::rInterface ()->issueCommand (getFullName () + '[' + QString::number (from_row + 1) + ':' + QString::number (to_row + 1) + "] <- " + data_string, RCommand::App | RCommand::Sync, QString::null, 0,0, chain);
@@ -371,23 +383,23 @@
 
 void RKVariable::cellChanged (int row) {
 	RK_TRACE (OBJECTS);
-	if (myData ()->immediate_sync) {
+	if (data->immediate_sync) {
 		writeData (row, row);
 	} else {
-		RK_ASSERT (myData ()->changes);
-		if ((myData ()->changes->from_index > row) || (myData ()->changes->from_index == -1)) myData ()->changes->from_index = row;
-		if (myData ()->changes->to_index < row) myData ()->changes->to_index = row;
+		RK_ASSERT (data->changes);
+		if ((data->changes->from_index > row) || (data->changes->from_index == -1)) data->changes->from_index = row;
+		if (data->changes->to_index < row) data->changes->to_index = row;
 	}
 }
 
 void RKVariable::cellsChanged (int from_row, int to_row) {
 	RK_TRACE (OBJECTS);
-	if (myData ()->immediate_sync) {
+	if (data->immediate_sync) {
 		writeData (from_row, to_row);
 	} else {
-		RK_ASSERT (myData ()->changes);
-		if ((myData ()->changes->from_index > from_row) || (myData ()->changes->from_index == -1)) myData ()->changes->from_index = from_row;
-		if (myData ()->changes->to_index < to_row) myData ()->changes->to_index = to_row;
+		RK_ASSERT (data->changes);
+		if ((data->changes->from_index > from_row) || (data->changes->from_index == -1)) data->changes->from_index = from_row;
+		if (data->changes->to_index < to_row) data->changes->to_index = to_row;
 	}
 }
 
@@ -395,48 +407,48 @@
 	RK_TRACE (OBJECTS);
 
 	if (length <= 0) length = 1;
-	if (length < (myData ()->allocated_length - 1)) {
+	if (length < (data->allocated_length - 1)) {
 		dimensions[0] = length;
 		return;
 	}
 
 	int ilength = length + 1;		// be a little generous
-	int target = myData ()->allocated_length;
+	int target = data->allocated_length;
 	if (!target) target = INITIAL_ALLOC;
 	while (target <= ilength) target = target * ALLOC_STEP;
-	RK_DO (qDebug ("resizing from %d to %d", myData ()->allocated_length, target), OBJECTS, DL_DEBUG);
+	RK_DO (qDebug ("resizing from %d to %d", data->allocated_length, target), OBJECTS, DL_DEBUG);
 
 	// allocate new memory and copy
 	if (getDataType () == RObject::DataCharacter) {
-		RK_ASSERT (myData ()->cell_doubles == 0);
+		RK_ASSERT (data->cell_doubles == 0);
 		QString *new_data = new QString[target];
-		if (myData ()->allocated_length) {		// if not yet allocated, don't mem-move
-			qmemmove (new_data, myData ()->cell_strings, myData ()->allocated_length * sizeof (QString));
+		if (data->allocated_length) {		// if not yet allocated, don't mem-move
+			qmemmove (new_data, data->cell_strings, data->allocated_length * sizeof (QString));
 		}
-		delete [] (myData ()->cell_strings);
-		myData ()->cell_strings = new_data;
+		delete [] (data->cell_strings);
+		data->cell_strings = new_data;
 	} else {
-		RK_ASSERT (myData ()->cell_strings == 0);
+		RK_ASSERT (data->cell_strings == 0);
 		double *new_data = new double[target];
-		if (myData ()->allocated_length) {		// if not yet allocated, don't mem-move
-			qmemmove (new_data, myData ()->cell_doubles, myData ()->allocated_length * sizeof (double));
+		if (data->allocated_length) {		// if not yet allocated, don't mem-move
+			qmemmove (new_data, data->cell_doubles, data->allocated_length * sizeof (double));
 		}
-		delete [] (myData ()->cell_doubles);
-		myData ()->cell_doubles = new_data;
+		delete [] (data->cell_doubles);
+		data->cell_doubles = new_data;
 	}
 	int *new_states = new int[target];
-	if (myData ()->allocated_length) {		// if not yet allocated, don't mem-move
-		qmemmove (new_states, myData ()->cell_states, myData ()->allocated_length * sizeof (int));
+	if (data->allocated_length) {		// if not yet allocated, don't mem-move
+		qmemmove (new_states, data->cell_states, data->allocated_length * sizeof (int));
 	}
-	delete [] (myData ()->cell_states);
-	myData ()->cell_states = new_states;
+	delete [] (data->cell_states);
+	data->cell_states = new_states;
 
 	// set allocated but unused rows to Unknown
-	for (int i=myData ()->allocated_length; i < target; ++i) {
-		myData ()->cell_states[i] = RKVarEditData::Unknown;
+	for (int i=data->allocated_length; i < target; ++i) {
+		data->cell_states[i] = RKVarEditData::Unknown;
 	}
 
-	myData ()->allocated_length = target;
+	data->allocated_length = target;
 	dimensions[0] = length;
 }
 
@@ -445,12 +457,12 @@
 
 	// TODO: downsizing to values other than 0
 	if (getLength () <= 0) {
-		delete [] myData ()->cell_doubles;
-		myData ()->cell_doubles = 0;
-		delete [] myData ()->cell_strings;
-		myData ()->cell_strings = 0;
-		delete [] myData ()->cell_states;
-		myData ()->cell_states = 0;
+		delete [] data->cell_doubles;
+		data->cell_doubles = 0;
+		delete [] data->cell_strings;
+		data->cell_strings = 0;
+		delete [] data->cell_states;
+		data->cell_states = 0;
 	}
 }
 
@@ -460,34 +472,34 @@
 		return (*unknown_char);
 	}
 
-	if (myData ()->cell_states[row] & RKVarEditData::Invalid) {
-		RK_ASSERT (myData ()->invalid_fields[row] != 0);
-		return (*(myData ()->invalid_fields[row]));
+	if (data->cell_states[row] & RKVarEditData::Invalid) {
+		RK_ASSERT (data->invalid_fields[row] != 0);
+		return (*(data->invalid_fields[row]));
 	}
 
-	if (myData ()->cell_states[row] & RKVarEditData::NA) {
+	if (data->cell_states[row] & RKVarEditData::NA) {
 		return (*na_char);
 	}
 
-	if (pretty && (myData ()->value_labels)) {
+	if (pretty && (data->value_labels)) {
 		QString otext = getText (row);
-		if (myData ()->value_labels->contains (otext)) {
-			return (*(myData ()->value_labels))[otext];
+		if (data->value_labels->contains (otext)) {
+			return (*(data->value_labels))[otext];
 		}
 	}
 
 	if (getDataType () == DataCharacter) {
-		RK_ASSERT (myData ()->cell_strings != 0);
-		return (myData ()->cell_strings[row]);
+		RK_ASSERT (data->cell_strings != 0);
+		return (data->cell_strings[row]);
 	} else {
-		RK_ASSERT (myData ()->cell_doubles != 0);
-		if (pretty && myData ()->formatting_options && (myData ()->formatting_options->precision_mode != FormattingOptions::PrecisionDefault)) {
-			if (myData ()->formatting_options->precision_mode == FormattingOptions::PrecisionRequired) {
-				return QString::number (myData ()->cell_doubles[row], 'g', MAX_PRECISION);
+		RK_ASSERT (data->cell_doubles != 0);
+		if (pretty && data->formatting_options && (data->formatting_options->precision_mode != FormattingOptions::PrecisionDefault)) {
+			if (data->formatting_options->precision_mode == FormattingOptions::PrecisionRequired) {
+				return QString::number (data->cell_doubles[row], 'g', MAX_PRECISION);
 			}
-			return QString::number (myData ()->cell_doubles[row], 'f', myData ()->formatting_options->precision);
+			return QString::number (data->cell_doubles[row], 'f', data->formatting_options->precision);
 		}
-		return QString::number (myData ()->cell_doubles[row], 'g', MAX_PRECISION);
+		return QString::number (data->cell_doubles[row], 'g', MAX_PRECISION);
 	}
 }
 
@@ -503,12 +515,12 @@
 	} else if (getDataType () == DataCharacter) {
 		return (rQuote (getText (row)));
 	} else if (getDataType () == DataLogical) {
-		RK_ASSERT (myData ()->cell_doubles != 0);
-		if (myData ()->cell_doubles[row] == 0) return ("FALSE");
+		RK_ASSERT (data->cell_doubles != 0);
+		if (data->cell_doubles[row] == 0) return ("FALSE");
 		else return ("TRUE");
 	} else {
-		RK_ASSERT (myData ()->cell_doubles != 0);
-		return (QString::number (myData ()->cell_doubles[row], 'g', MAX_PRECISION));
+		RK_ASSERT (data->cell_doubles != 0);
+		return (QString::number (data->cell_doubles[row], 'g', MAX_PRECISION));
 	}
 }
 
@@ -517,43 +529,43 @@
 	RK_ASSERT (row < getLength ());
 
 	// clear previous state
-	if (myData ()->cell_states[row] & RKVarEditData::Invalid) {
-		myData ()->cell_states[row] = RKVarEditData::UnsyncedInvalidState;
-		myData ()->invalid_fields.remove (row);
+	if (data->cell_states[row] & RKVarEditData::Invalid) {
+		data->cell_states[row] = RKVarEditData::UnsyncedInvalidState;
+		data->invalid_fields.remove (row);
 	} else {
-		myData ()->cell_states[row] = 0;
+		data->cell_states[row] = 0;
 	}
 
 	if (text.isNull ()) {
-		myData ()->cell_states[row] |= RKVarEditData::NA;
+		data->cell_states[row] |= RKVarEditData::NA;
 	} else {
 		if (getDataType () == DataCharacter) {
-			RK_ASSERT (myData ()->cell_strings != 0);
-			myData ()->cell_strings[row] = text;
-			myData ()->cell_states[row] |= RKVarEditData::Valid;
+			RK_ASSERT (data->cell_strings != 0);
+			data->cell_strings[row] = text;
+			data->cell_states[row] |= RKVarEditData::Valid;
 		} else if (getDataType () == DataFactor) {
-			RK_ASSERT (myData ()->cell_doubles != 0);
+			RK_ASSERT (data->cell_doubles != 0);
 			if (text.isEmpty ()) {
-				myData ()->cell_states[row] |= RKVarEditData::NA;
-			} else if (myData ()->value_labels && myData ()->value_labels->contains (text)) {
-				myData ()->cell_doubles[row] = text.toInt ();
-				myData ()->cell_states[row] |= RKVarEditData::Valid;
+				data->cell_states[row] |= RKVarEditData::NA;
+			} else if (data->value_labels && data->value_labels->contains (text)) {
+				data->cell_doubles[row] = text.toInt ();
+				data->cell_states[row] |= RKVarEditData::Valid;
 			} else {
-				myData ()->invalid_fields.replace (row, new QString (text));
-				myData ()->cell_states[row] |= RKVarEditData::Invalid | RKVarEditData::UnsyncedInvalidState;
+				data->invalid_fields.replace (row, new QString (text));
+				data->cell_states[row] |= RKVarEditData::Invalid | RKVarEditData::UnsyncedInvalidState;
 			}
 		} else {
-			RK_ASSERT (myData ()->cell_doubles != 0);
+			RK_ASSERT (data->cell_doubles != 0);
 			bool ok;
 			if (text.isEmpty ()) {
-				myData ()->cell_states[row] |= RKVarEditData::NA;
+				data->cell_states[row] |= RKVarEditData::NA;
 			} else {
-				myData ()->cell_doubles[row] = text.toDouble (&ok);
+				data->cell_doubles[row] = text.toDouble (&ok);
 				if (ok) {
-					myData ()->cell_states[row] |= RKVarEditData::Valid;
+					data->cell_states[row] |= RKVarEditData::Valid;
 				} else {
-					myData ()->invalid_fields.replace (row, new QString (text));
-					myData ()->cell_states[row] |= RKVarEditData::Invalid | RKVarEditData::UnsyncedInvalidState;
+					data->invalid_fields.replace (row, new QString (text));
+					data->cell_states[row] |= RKVarEditData::Invalid | RKVarEditData::UnsyncedInvalidState;
 				}
 			}
 		}
@@ -562,10 +574,10 @@
 }
 
 QString RKVariable::getLabeled (int row) {
-	if (myData ()->value_labels) {
+	if (data->value_labels) {
 		QString otext = getText (row);
-		if (myData ()->value_labels->contains (otext)) {
-			return (*(myData ()->value_labels))[otext];
+		if (data->value_labels->contains (otext)) {
+			return (*(data->value_labels))[otext];
 		}
 	}
 	return getText (row);
@@ -581,10 +593,10 @@
 
 	// TODO: no, this is not good. Return a _copy_!
 	// we simply return the whole array starting at the given offset for now. Change this, if the storage mechanism gets changed!
-	return &(myData ()->cell_doubles[from_row]);
+	return &(data->cell_doubles[from_row]);
 }
 
-void RKVariable::setNumeric (int from_row, int to_row, double *data) {
+void RKVariable::setNumeric (int from_row, int to_row, double *numdata) {
 	RK_TRACE (OBJECTS);
 	RK_ASSERT (to_row < getLength ());
 
@@ -592,33 +604,33 @@
 		RK_ASSERT (false);		// asserting false to catch cases of this use for now. it's not really a problem, though
 		int i = 0;
 		for (int row=from_row; row <= to_row; ++row) {
-			setText (row, QString::number (data[i++], 'g', MAX_PRECISION));
+			setText (row, QString::number (numdata[i++], 'g', MAX_PRECISION));
 		}
 	} else if (getDataType () == DataFactor) {
 		int i = 0;
 		for (int row=from_row; row <= to_row; ++row) {
-			if (myData ()->cell_states[row] & RKVarEditData::Invalid) myData ()->cell_states[row] =  RKVarEditData::UnsyncedInvalidState;
-			else myData ()->cell_states[row] = 0;
+			if (data->cell_states[row] & RKVarEditData::Invalid) data->cell_states[row] =  RKVarEditData::UnsyncedInvalidState;
+			else data->cell_states[row] = 0;
 
-			if (isnan (data[i]) || (!myData ()->value_labels) || (!myData ()->value_labels->contains (QString::number (data[i])))) {
-				myData ()->cell_states[row] |= RKVarEditData::NA;
+			if (isnan (numdata[i]) || (!data->value_labels) || (!data->value_labels->contains (QString::number (numdata[i])))) {
+				data->cell_states[row] |= RKVarEditData::NA;
 			} else {
-				myData ()->cell_states[row] |= RKVarEditData::Valid;
-				myData ()->cell_doubles[row] = data[i];
+				data->cell_states[row] |= RKVarEditData::Valid;
+				data->cell_doubles[row] = numdata[i];
 			}
 			++i;
 		}
 	} else {
 		int i = 0;
 		for (int row=from_row; row <= to_row; ++row) {
-			if (myData ()->cell_states[row] & RKVarEditData::Invalid) myData ()->cell_states[row] = RKVarEditData::UnsyncedInvalidState;
-			else myData ()->cell_states[row] = 0;
+			if (data->cell_states[row] & RKVarEditData::Invalid) data->cell_states[row] = RKVarEditData::UnsyncedInvalidState;
+			else data->cell_states[row] = 0;
 
-			if (isnan (data[i])) {
-				myData ()->cell_states[row] |= RKVarEditData::NA;
+			if (isnan (numdata[i])) {
+				data->cell_states[row] |= RKVarEditData::NA;
 			} else {
-				myData ()->cell_states[row] |= RKVarEditData::Valid;
-				myData ()->cell_doubles[row] = data[i];
+				data->cell_states[row] |= RKVarEditData::Valid;
+				data->cell_doubles[row] = numdata[i];
 			}
 			++i;
 		}
@@ -645,25 +657,25 @@
 	return ret;
 }
 
-void RKVariable::setCharacter (int from_row, int to_row, QString *data) {
+void RKVariable::setCharacter (int from_row, int to_row, QString *txtdata) {
 	RK_TRACE (OBJECTS);
 	RK_ASSERT (to_row < getLength ());
 	
 	if (getDataType () == DataCharacter) {
 		int i=0;
 		for (int row=from_row; row <= to_row; ++row) {
-			if (myData ()->cell_states[row] & RKVarEditData::Invalid) myData ()->cell_states[row] = RKVarEditData::UnsyncedInvalidState;
-			else myData ()->cell_states[row] = 0;
+			if (data->cell_states[row] & RKVarEditData::Invalid) data->cell_states[row] = RKVarEditData::UnsyncedInvalidState;
+			else data->cell_states[row] = 0;
 
-			if (data[i].isNull ()) myData ()->cell_states[row] |= RKVarEditData::NA;
-			else myData ()->cell_states[row] |= RKVarEditData::Valid;
+			if (txtdata[i].isNull ()) data->cell_states[row] |= RKVarEditData::NA;
+			else data->cell_states[row] |= RKVarEditData::Valid;
 
-			myData ()->cell_strings[row] = data[i++];
+			data->cell_strings[row] = txtdata[i++];
 		}
 	} else {
 		int i=0;
 		for (int row=from_row; row <= to_row; ++row) {
-			setText (row, data[i++]);
+			setText (row, txtdata[i++]);
 		}
 		return;
 	}
@@ -675,17 +687,17 @@
 	RK_ASSERT (to_row < getLength ());
 
 	if ((from_row < 0)) from_row = 0;
-	if ((to_row < 0)) to_row = myData ()->allocated_length - 1;
+	if ((to_row < 0)) to_row = data->allocated_length - 1;
 		
 	for (int row=from_row; row <= to_row; ++row) {
-		myData ()->cell_strings[row] = RKVarEditData::Unknown;
+		data->cell_strings[row] = RKVarEditData::Unknown;
 	}
 }
 
 RKVariable::Status RKVariable::cellStatus (int row) {
-	if (myData ()->cell_states[row] == RKVarEditData::Unknown) return ValueUnknown;
-	if (myData ()->cell_states[row] & RKVarEditData::NA) return ValueUnused;
-	if (myData ()->cell_states[row] & RKVarEditData::Invalid) return ValueInvalid;
+	if (data->cell_states[row] == RKVarEditData::Unknown) return ValueUnknown;
+	if (data->cell_states[row] & RKVarEditData::NA) return ValueUnused;
+	if (data->cell_states[row] & RKVarEditData::Invalid) return ValueInvalid;
 	return ValueValid;
 }
 
@@ -700,30 +712,30 @@
 	int offset = (to_row - from_row) + 1;
 
 	for (int row = from_row; row < getLength (); ++row) {
-		QString *dummy = myData ()->invalid_fields.take (row);
+		QString *dummy = data->invalid_fields.take (row);
 		if (dummy) {
 			if (!changed_invalids) changed_invalids = new Q3ValueList<int>;
 			changed_invalids->append (row);
 			if (row > to_row) {
 				changed_invalids->append (row - offset);
-				myData ()->invalid_fields.replace (row - offset, dummy);
+				data->invalid_fields.replace (row - offset, dummy);
 			} else {
 				delete dummy;
 			}
 		}
 	}
 
-	if (to_row < (myData ()->allocated_length - 1)) {	// not the last rows
-		if (myData ()->cell_strings) {
-			qmemmove (&(myData ()->cell_strings[from_row]), &(myData ()->cell_strings[to_row+1]), (myData ()->allocated_length - to_row - 1) * sizeof (QString));
+	if (to_row < (data->allocated_length - 1)) {	// not the last rows
+		if (data->cell_strings) {
+			qmemmove (&(data->cell_strings[from_row]), &(data->cell_strings[to_row+1]), (data->allocated_length - to_row - 1) * sizeof (QString));
 		} else {
-			qmemmove (&(myData ()->cell_doubles[from_row]), &(myData ()->cell_doubles[to_row+1]), (myData ()->allocated_length - to_row - 1) * sizeof (double));
+			qmemmove (&(data->cell_doubles[from_row]), &(data->cell_doubles[to_row+1]), (data->allocated_length - to_row - 1) * sizeof (double));
 		}
-		qmemmove (&(myData ()->cell_states[from_row]), &(myData ()->cell_states[to_row+1]), (myData ()->allocated_length - to_row - 1) * sizeof (int));
+		qmemmove (&(data->cell_states[from_row]), &(data->cell_states[to_row+1]), (data->allocated_length - to_row - 1) * sizeof (int));
 	}
 
-	for (int row = (myData ()->allocated_length - offset); row < myData ()->allocated_length; ++row) {
-		myData ()->cell_states[row] = RKVarEditData::Unknown;
+	for (int row = (data->allocated_length - offset); row < data->allocated_length; ++row) {
+		data->cell_states[row] = RKVarEditData::Unknown;
 	}
 
 	if (changed_invalids) {
@@ -748,32 +760,32 @@
 	extendToLength (getLength () + count);		// getLength is the new length after this!
 
 	for (int i=old_len; i < getLength(); ++i) {
-		myData ()->cell_states[i] = RKVarEditData::NA;
+		data->cell_states[i] = RKVarEditData::NA;
 	}
 
 	Q3ValueList<int> *changed_invalids = 0;
 	for (int i = getLength () - count - 1; i >= row; --i) {
-		QString *dummy = myData ()->invalid_fields.take (i);
+		QString *dummy = data->invalid_fields.take (i);
 		if (dummy) {
 			if (!changed_invalids) changed_invalids = new Q3ValueList<int>;
 			changed_invalids->append (i);
 			changed_invalids->append (i + count);
-			myData ()->invalid_fields.replace (i + count, dummy);
+			data->invalid_fields.replace (i + count, dummy);
 		}
 	}
 
 	if (row >= getLength () && (count == 1)) {		// important special case
-		if (myData ()->cell_strings) myData ()->cell_strings[row+count] = QString::null;
-		if (myData ()->cell_doubles) myData ()->cell_doubles[row+count] = 0.0;
-		myData ()->cell_states[row+count] = RKVarEditData::NA;
+		if (data->cell_strings) data->cell_strings[row+count] = QString::null;
+		if (data->cell_doubles) data->cell_doubles[row+count] = 0.0;
+		data->cell_states[row+count] = RKVarEditData::NA;
 	} else {
-		if (myData ()->cell_strings) qmemmove (&(myData ()->cell_strings[row+count]), &(myData ()->cell_strings[row]), (myData ()->allocated_length - (row + count) - 1) * sizeof (QString));
-		if (myData ()->cell_doubles) qmemmove (&(myData ()->cell_doubles[row+count]), &(myData ()->cell_doubles[row]), (myData ()->allocated_length - (row + count) - 1) * sizeof (double));
-		qmemmove (&(myData ()->cell_states[row+count]), &(myData ()->cell_states[row]), (myData ()->allocated_length - (row + count) - 1) * sizeof (int));
+		if (data->cell_strings) qmemmove (&(data->cell_strings[row+count]), &(data->cell_strings[row]), (data->allocated_length - (row + count) - 1) * sizeof (QString));
+		if (data->cell_doubles) qmemmove (&(data->cell_doubles[row+count]), &(data->cell_doubles[row]), (data->allocated_length - (row + count) - 1) * sizeof (double));
+		qmemmove (&(data->cell_states[row+count]), &(data->cell_states[row]), (data->allocated_length - (row + count) - 1) * sizeof (int));
 	}
 	
 	for (int i=row+count-1; i >= row; --i) {
-		myData ()->cell_states[i] = RKVarEditData::NA;
+		data->cell_states[i] = RKVarEditData::NA;
 	}
 
 	if (changed_invalids) {
@@ -785,17 +797,17 @@
 }
 
 RObject::ValueLabels *RKVariable::getValueLabels () {
-	RK_ASSERT (myData ());
-	return (myData ()->value_labels);
+	RK_ASSERT (data);
+	return (data->value_labels);
 }
 
 void RKVariable::setValueLabels (ValueLabels *labels) {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 	
-	if (labels != myData ()->value_labels) {
-		delete (myData ()->value_labels);
-		myData ()->value_labels = labels;
+	if (labels != data->value_labels) {
+		delete (data->value_labels);
+		data->value_labels = labels;
 	}
 
 	writeValueLabels (0);
@@ -825,14 +837,14 @@
 
 void RKVariable::writeValueLabels (RCommandChain *chain) {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 	
-	if (myData ()->value_labels) {
+	if (data->value_labels) {
 		int i = 1;
 		QString level_string = "c (";
-		while (myData ()->value_labels->contains (QString::number (i))) {
-			level_string.append (rQuote ((*(myData ()->value_labels))[QString::number (i)]));
-			if (myData ()->value_labels->contains (QString::number (++i))) {
+		while (data->value_labels->contains (QString::number (i))) {
+			level_string.append (rQuote ((*(data->value_labels))[QString::number (i)]));
+			if (data->value_labels->contains (QString::number (++i))) {
 				level_string.append (", ");
 			}
 		}
@@ -846,14 +858,14 @@
 
 QString RKVariable::getValueLabelString () {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 
-	if (myData ()->value_labels) {
+	if (data->value_labels) {
 		int i = 1;
 		QString level_string;
-		while (myData ()->value_labels->contains (QString::number (i))) {
-			level_string.append ((*(myData ()->value_labels))[QString::number (i)]);
-			if (myData ()->value_labels->contains (QString::number (++i))) {
+		while (data->value_labels->contains (QString::number (i))) {
+			level_string.append ((*(data->value_labels))[QString::number (i)]);
+			if (data->value_labels->contains (QString::number (++i))) {
 				level_string.append ("#,#");
 			}
 		}
@@ -866,7 +878,7 @@
 
 void RKVariable::setValueLabelString (const QString &string) {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 
 	QStringList list = QStringList::split ("#,#", string);
 	
@@ -886,20 +898,20 @@
 
 RKVariable::FormattingOptions *RKVariable::getFormattingOptions () {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 
-	return myData ()->formatting_options;
+	return data->formatting_options;
 }
 
 void RKVariable::setFormattingOptions (FormattingOptions *formatting_options) {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 	
-	if (formatting_options != myData ()->formatting_options) {
-		delete myData ()->formatting_options;
+	if (formatting_options != data->formatting_options) {
+		delete data->formatting_options;
 	}
 
-	myData ()->formatting_options = formatting_options;
+	data->formatting_options = formatting_options;
 
 	if (!formatting_options) {
 		setMetaProperty ("format", QString::null);
@@ -932,21 +944,21 @@
 
 QString RKVariable::getFormattingOptionsString () {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 
 	return getMetaProperty ("format");
 }
 
 void RKVariable::setFormattingOptionsString (const QString &string) {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 
 	setFormattingOptions (parseFormattingOptionsString (string));
 }
 
 RKVariable::FormattingOptions *RKVariable::parseFormattingOptionsString (const QString &string) {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 
 	FormattingOptions *formatting_options = new FormattingOptions;
 	formatting_options->alignment = FormattingOptions::AlignDefault;
@@ -998,10 +1010,10 @@
 
 /** returns alignment to use for this variable */
 RKVariable::CellAlign RKVariable::getAlignment () {
-	RK_ASSERT (myData ());
+	RK_ASSERT (data);
 	
-	if (myData ()->formatting_options && (myData ()->formatting_options->alignment != FormattingOptions::AlignDefault)) {
-		if (myData ()->formatting_options->alignment == FormattingOptions::AlignLeft) return AlignCellLeft;
+	if (data->formatting_options && (data->formatting_options->alignment != FormattingOptions::AlignDefault)) {
+		if (data->formatting_options->alignment == FormattingOptions::AlignLeft) return AlignCellLeft;
 		return AlignCellRight;
 	} else {
 	// TODO: use global (configurable) defaults, if not specified

Modified: branches/KDE4_port/rkward/core/rkvariable.h
===================================================================
--- branches/KDE4_port/rkward/core/rkvariable.h	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/core/rkvariable.h	2007-11-07 18:11:02 UTC (rev 2183)
@@ -140,7 +140,7 @@
 /** reimplemented from RObject to change the internal data storage mode, if the var is being edited */
 	bool updateType (RData *new_data);
 /** Extended from RObject::EditData to actually contain data. */
-	struct RKVarEditData : public EditData {
+	struct RKVarEditData {
 		QString *cell_strings;
 		double *cell_doubles;
 		enum CellState {
@@ -166,20 +166,20 @@
 		FormattingOptions *formatting_options;
 /// storage for invalid fields
 		Q3IntDict<QString> invalid_fields;
+/// how many models need our data?
+		int num_listeners;
 	};
+	RKVarEditData* data;
+
 /** reimplemented from RObject */
-	void allocateEditData (RKEditor *editor);
+	void beginEdit ();
 /** reimplemented from RObject */
-	void initializeEditDataToEmpty ();
-/** reimplemented from RObject */
-	void discardEditData ();
+	void endEdit ();
 private:
 /** changes the allocated storage to contain a least length elements. More data may be allocated than acutally needed. This function only ever does upsizing. */
 	void extendToLength (int length);
 /** changes the allocated storage to contain a least getLength elements. More data may be allocated than acutally needed. This function only ever does downsizing. */
 	void downSize ();
-/** convenience function to avoid typing static_cast... */
-	RKVarEditData *myData () { return static_cast<RKVarEditData*> (data); };
 /** takes care of syncing the given cell */
 	void cellChanged (int row);
 /** takes care of syncing the given range of cells */
@@ -193,6 +193,11 @@
 	FormattingOptions *parseFormattingOptionsString (const QString &string);
 /** tries to match a value-label to the value in the given cell. Returns the label, or - if there is no label - the original value in textual representation */
 	QString getLabeled (int row);
+
+/** allocate edit data (cells initialized to NAs) */
+	void allocateEditData ();
+/** discard edit data */
+	void discardEditData ();
 /////////////////// END: data-handling //////////////////////
 };
 

Modified: branches/KDE4_port/rkward/core/robject.cpp
===================================================================
--- branches/KDE4_port/rkward/core/robject.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/core/robject.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -40,7 +40,6 @@
 	RObject::name = name;
 	type = 0;
 	meta_map = 0;
-	data = 0;
 	classnames = 0;
 	num_classes = 0;
 	dimensions = new int[1];	// safe initialization
@@ -51,8 +50,6 @@
 RObject::~RObject () {
 	RK_TRACE (OBJECTS);
 
-	if (data) discardEditData ();
-
 	delete [] dimensions;
 	delete [] classnames;
 }
@@ -290,8 +287,8 @@
 	properties_change = updateDimensions (new_data->getStructureVector ()[4]);
 
 	if (properties_change) RKGlobals::tracker ()->objectMetaChanged (this);
-	if (data && (data->dirty)) updateDataFromR (0);
-	if (data) data->pending = false;
+	if (type & NeedDataUpdate) updateDataFromR (0);
+	if (isPending ()) type -= Pending;
 
 	return true;
 }
@@ -299,20 +296,20 @@
 //virtual
 void RObject::updateDataFromR (RCommandChain *) {
 	RK_TRACE (OBJECTS);
-	RK_ASSERT (data);
-	data->dirty = false;
+
+	type -= (type & NeedDataUpdate);
 }
 
+#warning probably we do not really need this. Rather we should always call updateDataFromR recursively, and that will take care of things. (make sure not to overwrite pending changes, though)
 void RObject::markDataDirty () {
 	RK_TRACE (OBJECTS);
 
-	if (data) data->dirty = true;
-	unsigned int ccount = numChildren ();
-	if (!ccount) return;
-
-	RObject **childcopy = children ();
-	for (unsigned int i = 0; i < ccount; ++i) {
-		childcopy[i]->markDataDirty ();
+	type |= NeedDataUpdate;
+	if (isContainer ()) {
+		RObjectMap children = static_cast<RContainerObject*> (this)->childmap;
+		for (int i = children.size () - 1; i >= 0; --i) {
+			children[i]->markDataDirty ();
+		}
 	}
 }
 
@@ -372,6 +369,8 @@
 	bool changed = false;
 	int new_type = new_data->getIntVector ()[0];
 	if (type & Misplaced) new_type |= Misplaced;
+	if (type & Pending) new_type |= Pending;	// NOTE: why don't we just clear the pending flag, here? Well, we don't want to generate a change notification for this. TODO: rethink the logic, and maybe use an appropriate mask
+	if (type & NeedDataUpdate) new_type |= NeedDataUpdate;
 	if (type != new_type) {
 		changed = true;
 		type = new_type;
@@ -530,81 +529,16 @@
 	return (copy.replace ("[\"", "$").replace ('[', "").replace ("\"]", "").replace (']', ""));
 }
 
-RKEditor *RObject::objectOpened () const {
-	RK_TRACE (OBJECTS);
-
-	if (!data) return 0;
-	return data->editor;
+//virtual
+void RObject::beginEdit () {
+	RK_ASSERT (false);
 }
 
-void RObject::setObjectOpened (RKEditor *editor, bool opened) {
-	RK_TRACE (OBJECTS);
-
-	// TODO: only for now! Currently only a single editor may operate on an object
-	if (opened) {
-		RK_ASSERT (!data);
-	} else {
-		RK_ASSERT (data);
-	}
-
-	if (opened) {
-		if (!data) {
-			allocateEditData (editor);
-			updateDataFromR (0);
-		}
-	} else {
-		discardEditData ();
-	}
+//virtual
+void RObject::endEdit () {
+	RK_ASSERT (false);
 }
 
-void RObject::setCreatedInEditor (RKEditor *editor) {
-	RK_TRACE (OBJECTS);
-
-	// TODO: only for now! Currently only a single editor may operate on an object
-	RK_ASSERT (!data);
-
-	if (!data) {
-		allocateEditData (editor);
-		initializeEditDataToEmpty ();
-	}
-	data->pending = true;
-}
-
-// virtual
-void RObject::allocateEditData (RKEditor *editor) {
-	RK_TRACE (OBJECTS);
-
-	// this assert should stay even when more than one editor is allowed per object. After all, the edit-data should only ever be allocated once!
-	RK_ASSERT (!data);
-	
-	data = new EditData;
-	data->editor = editor;
-	data->dirty = false;
-	data->pending = false;
-}
-
-bool RObject::isPending () const {
-	RK_TRACE (OBJECTS);
-
-	if (!data) return false;
-	return (data->pending);
-}
-
-// virtual
-void RObject::initializeEditDataToEmpty () {
-	RK_TRACE (OBJECTS);
-}
-
-// virtual
-void RObject::discardEditData () {
-	RK_TRACE (OBJECTS);
-
-	RK_ASSERT (data);
-	
-	delete data;
-	data = 0;
-}
-
 bool RObject::canEdit () const {
 	RK_TRACE (OBJECTS);
 

Modified: branches/KDE4_port/rkward/core/robject.h
===================================================================
--- branches/KDE4_port/rkward/core/robject.h	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/core/robject.h	2007-11-07 18:11:02 UTC (rev 2183)
@@ -62,7 +62,9 @@
 		Factor=2 << 14,
 		Character=3 << 14,
 		Logical=4 << 14,
-		DataTypeMask=Numeric | Factor | Character | Logical
+		DataTypeMask=Numeric | Factor | Character | Logical,
+		NeedDataUpdate=1 << 30,	/** < the object's data should be (re-) fetched from R */
+		Pending=1 << 31		/** < the object is pending, i.e. it has been created in the object list, but we have not seen it in R, yet. This is used by data editors to create the illusion that a new object was added immediately, while in fact it takes some time to create it in the backend. */
 	};
 
 	enum RDataType {
@@ -90,12 +92,15 @@
 	bool isContainer () const { return (type & (Container | Environment | Workspace)); };
 	bool isDataFrame () const { return (type & DataFrame); };
 	bool isVariable () const { return (type & Variable); };
+	/** see RObjectType */
 	bool isType (int type) const { return (RObject::type & type); };
 	bool hasMetaObject () const { return (type & HasMetaObject); };
+	/** see RObjectType::Pending */
+	bool isPending () const { return type & Pending; };
 
 /** trigger an update of this and all descendent objects */
 	virtual void updateFromR (RCommandChain *chain);
-/** fetch updated data from the backend. Default implementation does nothing except clearing the dirty flag */
+/** fetch updated data from the backend, if there are any listeners. Default implementation does nothing except clearing the dirty flag */
 	virtual void updateDataFromR (RCommandChain *chain);
 /** mark the data of this object and all of its children as dirty (recursively). Dirty data will be updated *after* the new structure update (if the object is opened for editing) */
 	void markDataDirty ();
@@ -136,11 +141,6 @@
 /** Returns the parent / container of this object. All objects have a parent except for the RObjectList (which returns 0) */
 	RContainerObject *getContainer () const { return (parent); };
 
-/** number of child objects. Always 0, reimplemented in RContainerObject */
-	virtual int numChildren () const { return 0; };
-/** array of child objects. Always 0, reimplemented in RContainerObject */
-	virtual RObject **children () const { return 0; };
-
 	RDataType getDataType () const { return (typeToDataType (type)); };
 	int getType () const { return type; };
 	static RDataType typeToDataType (int ftype) { return ((RDataType) ((ftype & DataTypeMask) >> 14)); };
@@ -165,14 +165,6 @@
 @param name_is_canonified internal parameter. Set to true, if the name to match is already canonfied (else it will be canonified internally) */
 	virtual void findObjectsMatching (const QString &partial_name, RObjectSearchMap *current_list, bool name_is_canonified=false) const;
 
-/** If the object is being edited, returns that editor (in the future probably a list of editors). Else returns 0 */
-	RKEditor *objectOpened () const;
-/** Tells the object it has been opened (opened=true) or closed (opened=false) by the given editor. If the object is opened by the first editor, it will
-automatically take care of fetching its data. When closed by all editors, takes care of de-allocating that memory. */
-	void setObjectOpened (RKEditor *editor, bool opened);
-/** similar to setObjectOpened, but tells the object it has been created in the given editor. Does not try to fetch data from the backend. */
-	void setCreatedInEditor (RKEditor *editor);
-
 /// For now, the ChangeSet only handles RKVariables!
 	struct ChangeSet {
 		int from_index;
@@ -235,23 +227,12 @@
 @param new_data The command. Make sure it really is the dims field of an .rk.get.structure-command to update classes *before* calling this function! WARNING: the new_data object may get changed during this call. Call canAccommodateStructure () before calling this function!
 @returns whether this caused any changes */
 	bool updateDimensions (RData *new_data);
-/** an instance of this struct is created, when the object is opened for editing. For one thing, it keeps track of which editor(s) are working on the object.
-In subclasses like RKVariable, the struct is extended to additionally hold the data of the object, etc. */
-	struct EditData {
-		RKEditor *editor;
-		bool dirty;
-		bool pending;		// maybe move to type instead
-	};
-/** see EditData. 0 if the object is not being edited. */
-	EditData *data;
-/** see EditData. Allocates the data member. To be reimplemented in classes that need more information in the EditData struct */
-	virtual void allocateEditData (RKEditor *editor);
-/** companion to allocateEditData (). Initializes the data to empty (NA). Default implementation does nothing. Reimplemented in derived classes. */
-	virtual void initializeEditDataToEmpty ();
-/** see above */
-	virtual void discardEditData ();
 
-	bool isPending () const;
+friend class RKModificationTracker;
+/** Notify the object that some model needs its data. The object should take care of fetching the data from the backend, unless it already has the data. The default implementation does nothing (raises an assert). */
+	virtual void beginEdit ();
+/** Notify the object that a model no longer needs its data. If there have been as many endEdit() as beginEdit() calls, the object should discard its data storage. The default implementation does nothing (raises an assert). */
+	virtual void endEdit ();
 
 	void rCommandDone (RCommand *command);
 };

Modified: branches/KDE4_port/rkward/dataeditor/editformatdialog.cpp
===================================================================
--- branches/KDE4_port/rkward/dataeditor/editformatdialog.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/dataeditor/editformatdialog.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -35,7 +35,7 @@
 EditFormatDialog::EditFormatDialog (QWidget *parent, RKVariable *var, int mode) : QDialog (parent) {
 	RK_TRACE (EDITOR);
 	RK_ASSERT (var);
-	RK_ASSERT (var->objectOpened ());
+//	RK_ASSERT (var->objectOpened ());
 
 	EditFormatDialog::var = var;
 	EditFormatDialog::mode = mode;

Modified: branches/KDE4_port/rkward/dataeditor/editlabelsdialog.cpp
===================================================================
--- branches/KDE4_port/rkward/dataeditor/editlabelsdialog.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/dataeditor/editlabelsdialog.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -222,7 +222,7 @@
 EditLabelsDialog::EditLabelsDialog (QWidget *parent, RKVariable *var, int mode) : KDialog (parent) {
 	RK_TRACE (EDITOR);
 	RK_ASSERT (var);
-	RK_ASSERT (var->objectOpened ());
+//	RK_ASSERT (var->objectOpened ());
 
 	EditLabelsDialog::var = var;
 	EditLabelsDialog::mode = mode;

Modified: branches/KDE4_port/rkward/dataeditor/rkeditor.cpp
===================================================================
--- branches/KDE4_port/rkward/dataeditor/rkeditor.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/dataeditor/rkeditor.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -24,7 +24,7 @@
 
 RKEditor::~RKEditor () {
 	RK_TRACE (EDITOR);
-	getObject ()->setObjectOpened (this, false);
+//	getObject ()->setObjectOpened (this, false);
 }
 
 QString RKEditor::getDescription () {

Modified: branches/KDE4_port/rkward/dataeditor/rkeditordataframe.cpp
===================================================================
--- branches/KDE4_port/rkward/dataeditor/rkeditordataframe.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/dataeditor/rkeditordataframe.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -77,9 +77,9 @@
 	flushEdit ();
 	RKEditor::object = object;
 	if (initialize_to_empty) {
-		object->setCreatedInEditor (this);
+//		object->setCreatedInEditor (this);
 	} else {
-		object->setObjectOpened (this, true);
+//		object->setObjectOpened (this, true);
 	}
 
 	enableEditing (false);
@@ -91,7 +91,7 @@
 			if (obj->isVariable ()) {
 				static_cast<RKVariable*> (obj)->setLength (dataview->numTrueRows ());
 				setColObject (i, static_cast<RKVariable*> (obj));
-				obj->setCreatedInEditor (this);
+//				obj->setCreatedInEditor (this);
 			} else {
 				RK_ASSERT (false);
 			}
@@ -99,10 +99,11 @@
 		pushTable (open_chain);
 	}
 
+	// trigger fetching of the edit data
+	object->markDataDirty ();
+	object->updateFromR (open_chain);
 	// KDE4 TODO: this is no longer needed, as objects can now be addressed by their position in the parent
 	// actually, given the object, we already know the child-names. We don't know their order, however, so we better fetch the name-row again.
-	object->markDataDirty ();
-	object->updateFromR (open_chain);
 	RCommand *command = new RCommand ("names (" + object->getFullName () + ')', RCommand::Sync | RCommand::GetStringVector, QString::null, this, GET_NAMES_COMMAND);
 	RKGlobals::rInterface ()->issueCommand (command, open_chain);
 
@@ -131,7 +132,7 @@
 			if (current_child->isVariable ()) {
 				if (!getColObject (col)) {		// if we initialized the table to empty, the object may already exist in our map
 					setColObject (col, current_child);
-					current_child->setObjectOpened (this, true);
+//					current_child->setObjectOpened (this, true);
 				} else {
 					RK_ASSERT (getColObject (col) == current_child);
 				}
@@ -191,7 +192,7 @@
 	RK_ASSERT (obj->isVariable ());
 	RKGlobals::rInterface ()->issueCommand (new RCommand (".rk.data.frame.insert.column (" + getObject ()->getFullName () + ", \"" + obj->getShortName () + "\", " + QString::number (col+1) + ")", RCommand::App | RCommand::Sync));
 	static_cast<RKVariable*> (obj)->setLength (dataview->numTrueRows ());
-	obj->setCreatedInEditor (this);
+//	obj->setCreatedInEditor (this);
 
 	// TODO: find a nice way to update the list:
 	RK_ASSERT (col <= (numTrueCols () - 1));
@@ -229,7 +230,7 @@
 	// for now:
 	if (col < 0) return;
 
-	object->setObjectOpened (this, false);
+//	object->setObjectOpened (this, false);
 	
 	for (int i=(col+1); i < numTrueCols (); ++i) {
 		setColObject (i-1, getColObject (i));
@@ -268,7 +269,7 @@
 	insertNewColumn ();
 	if (object->isVariable ()) {
 		setColObject (numTrueCols () - 1, static_cast<RKVariable*> (object));
-		object->setObjectOpened (this, true);
+//		object->setObjectOpened (this, true);
 	} else {
 		RK_ASSERT (false);
 	}

Modified: branches/KDE4_port/rkward/dataeditor/rkvareditmodel.cpp
===================================================================
--- branches/KDE4_port/rkward/dataeditor/rkvareditmodel.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/dataeditor/rkvareditmodel.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -25,18 +25,18 @@
 
 #include "../debug.h"
 
-RKVarEditModel::RKVarEditModel (QObject *parent) : QAbstractTableModel (parent) {
+RKVarEditModel::RKVarEditModel (QObject *parent) : QAbstractTableModel (parent), RObjectListener (RObjectListener::DataModel, RObjectListener::ObjectRemoved) {
 	RK_TRACE (EDITOR);
 
 	meta_model = 0;
 	trailing_rows = trailing_cols = 0;
 	edit_blocks = 0;
-
-	connect (RKGlobals::tracker (), SIGNAL (objectRemoved(RObject*)), this, SLOT (objectRemoved(RObject*)));
 }
 
 RKVarEditModel::~RKVarEditModel () {
 	RK_TRACE (EDITOR);
+
+	for (int i = objects.size () - 1; i >= 0; --i) stopListenForObject (objects[i]);
 }
 
 void RKVarEditModel::addObject (int index, RKVariable* object) {
@@ -47,8 +47,9 @@
 
 	beginInsertColumns (QModelIndex (), index, index);
 	if (meta_model) meta_model->beginAddDataObject (index);
+	listenForObject (object);
 	objects.insert (index, object);
-	if (meta_model) meta_model->endAddDataObject (index);
+	if (meta_model) meta_model->endAddDataObject ();
 	endInsertColumns ();
 }
 
@@ -60,9 +61,13 @@
 
 	beginRemoveColumns (QModelIndex (), index, index);
 	if (meta_model) meta_model->beginRemoveDataObject (index);
-	objects.removeAt (index);
-	if (meta_model) meta_model->endRemoveDataObject (index);
+	stopListenForObject (objects.takeAt (index));
+	if (meta_model) meta_model->endRemoveDataObject ();
 	endRemoveColumns ();
+
+	if (objects.isEmpty ()) {
+#warning TODO notify editor
+	}
 }
 
 void RKVarEditModel::doInsertColumn (int) {
@@ -187,7 +192,7 @@
 	}
 
 	if (!edit_blocks) flags |= Qt::ItemIsEditable | Qt::ItemIsEnabled;
-	if ((col < objects.size ()) && (row >= objects[0]->getLength ())) flags |= Qt::ItemIsSelectable;
+	if ((col < objects.size ()) && (row < objects[0]->getLength ())) flags |= Qt::ItemIsSelectable;
 
 	return flags;
 }
@@ -237,12 +242,249 @@
 }
 
 
+/////////////////// RKVarEditMetaModel ////////////////////////
 
+RKVarEditMetaModel::RKVarEditMetaModel (RKVarEditModel* data_model) : QAbstractTableModel (data_model) {
+	RK_TRACE (EDITOR);
+	RK_ASSERT (data_model);
 
+	RKVarEditMetaModel::data_model = data_model;
+}
 
+RKVarEditMetaModel::~RKVarEditMetaModel () {
+	RK_TRACE (EDITOR);
+}
 
+void RKVarEditMetaModel::beginAddDataObject (int index) {
+	RK_TRACE (EDITOR);
 
+	beginInsertColumns (QModelIndex (), index, index);
+}
 
+void RKVarEditMetaModel::endAddDataObject () {
+	RK_TRACE (EDITOR);
 
+	endInsertColumns ();
+}
 
+void RKVarEditMetaModel::beginRemoveDataObject (int index) {
+	RK_TRACE (EDITOR);
+
+	beginRemoveColumns (QModelIndex (), index, index);
+}
+
+void RKVarEditMetaModel::endRemoveDataObject () {
+	RK_TRACE (EDITOR);
+
+	endRemoveColumns ();
+}
+
+int RKVarEditMetaModel::rowCount (const QModelIndex& parent) const {
+	RK_TRACE (EDITOR);
+
+	if (parent.isValid ()) return 0;
+	return RowCount;
+}
+
+int RKVarEditMetaModel::columnCount (const QModelIndex& parent) const {
+	RK_TRACE (EDITOR);
+
+	return (data_model->columnCount (parent));
+}
+
+QVariant RKVarEditMetaModel::data (const QModelIndex& index, int role) const {
+	RK_TRACE (EDITOR);
+
+	if (!index.isValid ()) return QVariant ();
+	int row = index.row ();
+	int col = index.column ();
+	if ((col >= data_model->apparentCols ()) || (row >= RowCount)) {
+		RK_ASSERT (false);
+		return QVariant ();
+	}
+
+	// on a trailing col
+	if (col >= data_model->objects.size ()) {
+		if (role == Qt::BackgroundRole) return (Qt::gray);
+		if (role == Qt::ToolTipRole) return (i18n ("Type on these fields to add new columns"));
+		return QVariant ();
+	}
+
+	if ((role == Qt::DisplayRole) || (role == Qt::EditRole)) {
+		RKVariable *var = data_model->objects[col];
+		RK_ASSERT (var);
+
+		if (row == NameRow) return var->getShortName ();
+		if (row == LabelRow) return var->getLabel ();
+		if (row == TypeRow) {
+			if (role == Qt::EditRole) return QString::number (var->getDataType ());
+			return RObject::typeToText (var->getDataType ());
+		}
+		if (row == FormatRow) return var->getFormattingOptionsString ();
+		if (row == LevelsRow) return var->getValueLabelString ();
+	}
+
+	return QVariant ();
+}
+
+Qt::ItemFlags RKVarEditMetaModel::flags (const QModelIndex& index) const {
+	RK_TRACE (EDITOR);
+
+	Qt::ItemFlags flags = 0;
+
+	if (!index.isValid ()) return flags;
+	int row = index.row ();
+	int col = index.column ();
+	if ((col >= data_model->apparentCols ()) || (row >= RowCount)) {
+		RK_ASSERT (false);
+		return flags;
+	}
+
+	if (!data_model->edit_blocks) flags |= Qt::ItemIsEditable | Qt::ItemIsEnabled;
+	if ((col < data_model->objects.size ()) && (row < RowCount)) flags |= Qt::ItemIsSelectable;
+
+	return flags;
+}
+
+bool RKVarEditMetaModel::setData (const QModelIndex& index, const QVariant& value, int role) {
+	RK_TRACE (EDITOR);
+
+	if (!index.isValid ()) return false;
+	int row = index.row ();
+	int col = index.column ();
+	if (data_model->edit_blocks || (role != Qt::EditRole) || (col >= data_model->apparentCols ()) || (row >= RowCount)) {
+		RK_ASSERT (false);
+		return false;
+	}
+
+	if (col >= data_model->objects.size ()) {		// trailing col
+		// somebody should add a column for us
+		data_model->doInsertColumn (data_model->objects.size ());
+
+		if (col >= data_model->objects.size ()) {
+			// apparently, no column has been added in the above signal
+			return false;
+		}
+	}
+
+	// edit of normal cells
+	RKVariable* var = data_model->objects[col];
+	RK_ASSERT (var);
+
+	if (row == NameRow) {
+		if (var->getShortName () != value.toString ()) {
+			RKGlobals::tracker ()->renameObject (var, var->getContainer ()->validizeName (value.toString ()));
+		}
+	} else if (row == LabelRow) {
+		var->setLabel (value.toString (), true);
+	} else if (row == TypeRow) {
+		var->setVarType ((RObject::RDataType) value.toInt ());
+	} else if (row == FormatRow) {
+		var->setFormattingOptionsString (value.toString ());
+	} else if (row == LevelsRow) {
+		if (value.toString () != var->getValueLabelString ()) {
+			var->setValueLabelString (value.toString ());
+		}
+	}
+
+	return true;
+}
+
+QVariant RKVarEditMetaModel::headerData (int section, Qt::Orientation orientation, int role) const {
+	RK_TRACE (EDITOR);
+
+	if (orientation == Qt::Horizontal) {
+		return data_model->headerData (section, orientation, role);
+	}
+
+	if (role == Qt::DisplayRole) {
+		if (section == NameRow) return (i18n ("Name"));
+		if (section == LabelRow) return (i18n ("Label"));
+		if (section == TypeRow) return (i18n ("Type"));
+		if (section == FormatRow) return (i18n ("Format"));
+		if (section == LevelsRow) return (i18n ("Levels"));
+	}
+
+	return QVariant ();
+}
+
+
+/////////////////// RKVarEditDataFrameModel ////////////////////////
+
+
+RKVarEditDataFrameModel::RKVarEditDataFrameModel (RContainerObject* dataframe, QObject *parent) : RKVarEditModel (parent) {
+	RK_TRACE (EDITOR);
+
+	RKVarEditDataFrameModel::dataframe = dataframe;
+
+	trailing_rows = 1;
+	trailing_cols = 1;
+
+	addNotificationType (RObjectListener::ChildAdded);
+	listenForObject (dataframe);
+
+	for (int i = 0; i < dataframe->numChildren (); ++i) {
+		RObject* obj = dataframe->findChildByIndex (i);
+		RK_ASSERT (obj);
+		RK_ASSERT (obj->isVariable ());
+		addObject (i, static_cast<RKVariable*> (obj));
+	}
+}
+
+RKVarEditDataFrameModel::~RKVarEditDataFrameModel () {
+	RK_TRACE (EDITOR);
+
+	if (dataframe) stopListenForObject (dataframe);
+}
+
+bool RKVarEditDataFrameModel::insertColumns (int column, int count, const QModelIndex& parent) {
+	RK_TRACE (EDITOR);
+
+/*	RObject *obj = static_cast<RContainerObject *> (getObject ())->createNewChild (static_cast<RContainerObject *> (getObject ())->validizeName (QString ()), col, this);
+	RK_ASSERT (obj->isVariable ());
+	RKGlobals::rInterface ()->issueCommand (new RCommand (".rk.data.frame.insert.column (" + getObject ()->getFullName () + ", \"" + obj->getShortName () + "\", " + QString::number (col+1) + ")", RCommand::App | RCommand::Sync));
+	static_cast<RKVariable*> (obj)->setLength (dataview->numTrueRows ());
+	obj->setCreatedInEditor (this); */
+
+#warning TODO implement
+}
+
+bool RKVarEditDataFrameModel::removeColumns (int column, int count, const QModelIndex& parent) {
+	RK_TRACE (EDITOR);
+
+/*	RKGlobals::tracker ()->removeObject (obj); */
+#warning TODO implement
+}
+
+void RKVarEditDataFrameModel::objectRemoved (RObject* object) {
+	RK_TRACE (EDITOR);
+
+	if (object == dataframe) {
+		while (!objects.isEmpty ()) RKVarEditModel::objectRemoved (objects[0]);
+#warning TODO: notify editor
+		stopListenForObject (dataframe);
+		dataframe = 0;
+	}
+
+	RKVarEditModel::objectRemoved (object);
+}
+
+void RKVarEditDataFrameModel::childAdded (int index, RObject* parent) {
+	RK_TRACE (EDITOR);
+
+	if (parent == dataframe) {
+		RObject* child = dataframe->findChildByIndex (index);
+		RK_ASSERT (child);
+
+		if (child->isVariable ()) addObject (index, static_cast<RKVariable*> (child));
+		else RK_ASSERT (false);
+	}
+}
+
+void RKVarEditDataFrameModel::doInsertColumn (int index) {
+	RK_TRACE (EDITOR);
+
+	insertColumns (index, 1);
+}
+
 #include "rkvareditmodel.moc"

Modified: branches/KDE4_port/rkward/dataeditor/rkvareditmodel.h
===================================================================
--- branches/KDE4_port/rkward/dataeditor/rkvareditmodel.h	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/dataeditor/rkvareditmodel.h	2007-11-07 18:11:02 UTC (rev 2183)
@@ -22,16 +22,23 @@
 #include <QList>
 
 #include "../core/rkvariable.h"
+#include "../core/rkmodificationtracker.h"
 
 class RKVarEditMetaModel;
+class RKEditor;
 
-/** This class represents a collection of RKVariables of uniform length (typically a data.frame) suitable for editing in a model/view editor such as QTableView. Probably it will only ever support editing a single RKVariable, though, as it is not possible to ensure uniform length outside of a data.frame. For a data.frame use RKVarEditDataFrameModel . */
-class RKVarEditModel : public QAbstractTableModel {
+/** This class represents a collection of RKVariables of uniform length (typically a data.frame) suitable for editing in a model/view editor such as QTableView. Probably it will only ever support editing a single RKVariable, though, as it is not possible to ensure uniform length outside of a data.frame. For a data.frame use RKVarEditDataFrameModel . Since the real data storage is in RKVariable, it is ok (and recommended) to create separate models for separate editors/viewers, even if the objects in question are the same. */
+class RKVarEditModel : public QAbstractTableModel, public RObjectListener {
 	Q_OBJECT
 public:
 	RKVarEditModel (QObject *parent);
 	~RKVarEditModel ();
 
+	/** set the editor that is using this model. This is useful to find out, e.g. which window should be raised, when calling "Edit" on an object represented in this data-model. Also, the editor will be notified, if all objects in the model have been removed. */
+	void setEditor (RKEditor* editor) { myeditor = editor; };
+	/** see setEditor () */
+	RKEditor* getEditor () const { return myeditor; };
+
 	/** Add an object to the model at the given index. Calls this only once, unless from an RKVarEditDataFrameModel. You should add at least one object, **before** you add this model to any view.
 	@param index position to insert at, or -1 to append the item
 	@param object the objects to insert */
@@ -48,12 +55,6 @@
 	Qt::ItemFlags flags (const QModelIndex& index) const;
 	bool setData (const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
 	QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
-signals:
-	// convenience signal to tell the editor to block editing entirely, without having to set all flags to non-editable.
-	void blockEdit (bool block);
-public slots:
-	/** Receives notifications of object removals. Takes care of removing the object from the list. */
-	virtual void objectRemoved (RObject* object);
 protected:
 friend class RKVarEditMetaModel;
 	QList<RKVariable*> objects;
@@ -63,6 +64,9 @@
 	/** very simple convenience function to return the number of true rows + trailing rows */
 	int apparentRows () const { return (trailing_rows + objects.isEmpty () ? 0 : objects[0]->getLength ()); };
 
+	/** Receives notifications of object removals. Takes care of removing the object from the list. */
+	void objectRemoved (RObject* object);
+
 	/** insert a new column at index. Default implementation does nothing. To be implemented in subclasses */
 	virtual void doInsertColumn (int index);
 
@@ -72,12 +76,22 @@
 	int edit_blocks;
 
 	RKVarEditMetaModel* meta_model;
+	RKEditor* myeditor;
 };
 
 /** Represents the meta information portion belonging to an RKVarEditModel. Implemented in a separate class for technical reasons, only (so this info can be displayed in a separate QTableView). This model mostly acts as a slave of an RKVarEditModel. You will not need to call any functions directly except from the RKVarEditModel, or an item view. */
 class RKVarEditMetaModel : public QAbstractTableModel {
 	Q_OBJECT
 public:
+	enum Rows {
+		NameRow=0,
+		LabelRow,
+		TypeRow,
+		FormatRow,
+		LevelsRow,
+		RowCount = LevelsRow + 1
+	};
+
 	// QAbstractTableModel implementations
 	int rowCount (const QModelIndex& parent = QModelIndex()) const;
 	int columnCount (const QModelIndex& parent = QModelIndex()) const;
@@ -87,13 +101,13 @@
 	QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
 protected:
 friend class RKVarEditModel;
-	RKVarEditMetaModel (RKVarEditModel* datamodel);
+	RKVarEditMetaModel (RKVarEditModel* data_model);
 	~RKVarEditMetaModel ();
 
 	void beginAddDataObject (int index);
-	void endAddDataObject (int index);
+	void endAddDataObject ();
 	void beginRemoveDataObject (int index);
-	void endRemoveDataObject (int index);
+	void endRemoveDataObject ();
 
 	RKVarEditModel* data_model;
 };
@@ -107,12 +121,13 @@
 
 	bool insertColumns (int column, int count, const QModelIndex& parent = QModelIndex());
 	bool removeColumns (int column, int count, const QModelIndex& parent = QModelIndex());
+protected:
+	void doInsertColumn (int index);
+	/** reimplemented from RKVarEditModel to listen for the dataframe object as well */
+	void objectRemoved (RObject* object);
+	/** receives notifications of new objects added to this data.frame */
+	void childAdded (int index, RObject* parent);
 
-	// reimplemented from RKVarEditModel to list for the dataframe object as well
-	void objectRemoved (RObject* object);
-public slots:
-	void objectAdded (RObject* object);
-protected:
 	RContainerObject* dataframe;
 };
 

Modified: branches/KDE4_port/rkward/dataeditor/twintable.cpp
===================================================================
--- branches/KDE4_port/rkward/dataeditor/twintable.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/dataeditor/twintable.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -120,11 +120,11 @@
 	delete top_header_menu;
 	delete left_header_menu;
 	
-	for (int i=0; i < numTrueCols (); ++i) {
+/*	for (int i=0; i < numTrueCols (); ++i) {
 		RObject *object = getColObject (i);
 		if (object) object->setObjectOpened (this, false);
 		else RK_ASSERT (false);
-	}
+	} */
 }
 
 void TwinTable::scrolled (int x, int) {

Modified: branches/KDE4_port/rkward/plugin/rkcomponentproperties.cpp
===================================================================
--- branches/KDE4_port/rkward/plugin/rkcomponentproperties.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/plugin/rkcomponentproperties.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -614,20 +614,18 @@
 #include "../core/rkmodificationtracker.h"
 #include "../misc/rkobjectlistview.h"
 
-RKComponentPropertyRObjects::RKComponentPropertyRObjects (QObject *parent, bool required) : RKComponentPropertyBase (parent, required) {
+RKComponentPropertyRObjects::RKComponentPropertyRObjects (QObject *parent, bool required) : RKComponentPropertyBase (parent, required), RObjectListener (RObjectListener::Other, RObjectListener::ObjectRemoved | RObjectListener::MetaChanged) {
 	RK_TRACE (PLUGIN);
 
 // no initial requirements
 	dims = min_length = max_length = min_num_objects = min_num_objects_if_any = max_num_objects = -1;
 	separator = "\n";
-
-// get notifications about changed/removed objects
-	connect (RKGlobals::tracker (), SIGNAL (objectRemoved (RObject *)), this, SLOT (removeObjectValue (RObject *)));
-	connect (RKGlobals::tracker (), SIGNAL (objectPropertiesChanged (RObject *)), this, SLOT (objectPropertiesChanged (RObject *)));
 }
 
 RKComponentPropertyRObjects::~RKComponentPropertyRObjects () {
 	RK_TRACE (PLUGIN);
+
+	setObjectValue (0);
 }
 
 void RKComponentPropertyRObjects::setListLength (int min_num_objects, int min_num_objects_if_any, int max_num_objects) {
@@ -643,9 +641,10 @@
 bool RKComponentPropertyRObjects::addObjectValue (RObject *object) {
 	RK_TRACE (PLUGIN);
 
-	if (isObjectValid (object)) {
+	if (object && isObjectValid (object)) {
 		if (!object_list.contains (object)) {
 			object_list.append (object);
+			listenForObject (object);
 			checkListLengthValid ();
 			emit (valueChanged (this));
 		}
@@ -654,10 +653,11 @@
 	return false;
 }
 
-void RKComponentPropertyRObjects::removeObjectValue (RObject *object) {
+void RKComponentPropertyRObjects::objectRemoved (RObject *object) {
 	RK_TRACE (PLUGIN);
 
 	if (object_list.removeAll (object)) {
+		stopListenForObject (object);
 		checkListLengthValid ();
 		emit (valueChanged (this));
 	}
@@ -689,7 +689,9 @@
 bool RKComponentPropertyRObjects::setObjectValue (RObject *object) {
 	RK_TRACE (PLUGIN);
 
-	object_list.clear ();
+	while (!object_list.isEmpty ()) {
+		stopListenForObject (object_list.takeAt (0));
+	}
 	return (addObjectValue (object));
 }
 
@@ -701,7 +703,7 @@
 	// remove items from the old list that are not in the new list
 	for (int i = 0; i < object_list.size (); ++i) {
 		if (!newlist.contains (object_list[i])) {
-			object_list.removeAt (i);
+			stopListenForObject (object_list.takeAt (i));
 			--i;
 			changes = true;
 		}
@@ -713,6 +715,7 @@
 		if (!object_list.contains (obj)) {
 			if (isObjectValid (obj)) {
 				object_list.append (obj);
+				listenForObject (obj);
 				changes = true;
 			}
 		}
@@ -811,6 +814,7 @@
 		RObject *obj = RObjectList::getObjectList ()->findObject (value);
 		if (obj && isObjectValid (obj)) {
 			object_list.append (obj);
+			listenForObject (obj);
 		} else {
 			ok = false;
 		}
@@ -935,14 +939,14 @@
 	}
 }
 
-void RKComponentPropertyRObjects::objectPropertiesChanged (RObject *object) {
+void RKComponentPropertyRObjects::objectMetaChanged (RObject *object) {
 	RK_TRACE (PLUGIN);
 
 	// if object list contains this object, check whether it is still valid. Otherwise remove it, revalidize and signal change.
 	int index = object_list.indexOf (object);
 	if (index >= 0) {
 		if (!isObjectValid (object)) {
-			object_list.removeAt (index);
+			stopListenForObject (object_list.takeAt (index));
 			checkListLengthValid ();
 			emit (valueChanged (this));
 		}
@@ -956,7 +960,7 @@
 
 	for (int i = 0; i < object_list.size (); ++i) {
 		if (!isObjectValid (object_list[i])) {
-			object_list.removeAt (i);
+			stopListenForObject (object_list.takeAt (i));
 			--i;
 			changes = true;
 		}

Modified: branches/KDE4_port/rkward/plugin/rkcomponentproperties.h
===================================================================
--- branches/KDE4_port/rkward/plugin/rkcomponentproperties.h	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/plugin/rkcomponentproperties.h	2007-11-07 18:11:02 UTC (rev 2183)
@@ -215,9 +215,10 @@
 #include <qlist.h>
 
 #include "../core/robject.h"
+#include "../core/rkmodificationtracker.h"
 
 /** special type of RKComponentProperty, that prepresents one or more RObject (s) */
-class RKComponentPropertyRObjects : public RKComponentPropertyBase {
+class RKComponentPropertyRObjects : public RKComponentPropertyBase, public RObjectListener {
 	Q_OBJECT
 public:
 /** constructor */
@@ -271,11 +272,12 @@
 	void governorValueChanged (RKComponentPropertyBase *property);
 /** @returns true, if the property holds the maximum number of items (or more) */
 	bool atMaxLength ();
-public slots:
-/** remove an object value. to be connected to RKModificationTracker::objectRemoved (). This is so we get notified if the object currently selected is removed TODO: is this effectively a duplication of setFromList? */
-	void removeObjectValue (RObject *object);
-/** to be connected to RKModificationTracker::objectPropertiesChanged (). This is so we get notified if the object currently selected is changed */
-	void objectPropertiesChanged (RObject *object);
+	void removeObjectValue (RObject* object) { objectRemoved (object); };
+protected:
+/** remove an object value. reimplemented from RObjectListener::objectRemoved (). This is so we get notified if the object currently selected is removed TODO: is this effectively a duplication of setFromList? */
+	void objectRemoved (RObject *removed);
+/** reimplemented from RObjectListener::objectMetaChanged (). This is so we get notified if the object currently selected is changed */
+	void objectMetaChanged (RObject *changed);
 private:
 /** check all objects currently in the list for validity. Remove invalid objects. Determine validity state depending on how many (valid) objects remain in the list. If the list was changed during validation, and silent!=false a valueChanged () signal is emitted */
 	void validizeAll (bool silent=false);

Modified: branches/KDE4_port/rkward/robjectviewer.cpp
===================================================================
--- branches/KDE4_port/rkward/robjectviewer.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/robjectviewer.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -43,10 +43,12 @@
 #define SUMMARY_COMMAND 1
 #define PRINT_COMMAND 2
 
-RObjectViewer::RObjectViewer (QWidget *parent, RObject *object) : RKMDIWindow (parent, RKMDIWindow::ObjectWindow, false) {
+RObjectViewer::RObjectViewer (QWidget *parent, RObject *object) : RKMDIWindow (parent, RKMDIWindow::ObjectWindow, false), RObjectListener (RObjectListener::ObjectView, RObjectListener::ObjectRemoved) {
+// KDE 4: TODO might listen for object meta / data changes as well
 	RK_TRACE (APP);
 	RK_ASSERT (object);
 	_object = object;
+	listenForObject (_object);
 
 	Q3VBoxLayout *layout = new Q3VBoxLayout (this);
 	Q3ScrollView *wrapper = new Q3ScrollView (this);
@@ -94,12 +96,13 @@
 	setCaption (i18n("Object Viewer: ") + object->getShortName ());
 	//resize (minimumSizeHint ().expandedTo (QSize (640, 480)));
 	update ();
-	connect (RKGlobals::tracker (), SIGNAL (objectRemoved(RObject*)), this, SLOT (objectRemoved(RObject*)));
 	show ();
 }
 
 RObjectViewer::~RObjectViewer () {
 	RK_TRACE (APP);
+
+	if (_object) stopListenForObject (_object);
 }
 
 void RObjectViewer::toggleSummary () {
@@ -134,6 +137,7 @@
 
 void RObjectViewer::update () {
 	RK_TRACE (APP);
+	if (!_object) return;
 
 	status_label->setText (i18n ("Fetching information"));
 	cancel_button->setEnabled (true);
@@ -165,6 +169,10 @@
 	if (object == _object) {
 		status_label->setText (i18n ("<b>Object was deleted!</b>"));
 		update_button->setEnabled (false);
+		stopListenForObject (_object);
+		_object = 0;
+	} else {
+		RK_ASSERT (false);
 	}
 }
 

Modified: branches/KDE4_port/rkward/robjectviewer.h
===================================================================
--- branches/KDE4_port/rkward/robjectviewer.h	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/robjectviewer.h	2007-11-07 18:11:02 UTC (rev 2183)
@@ -24,6 +24,7 @@
 #include <QLabel>
 
 #include "rbackend/rcommandreceiver.h"
+#include "core/rkmodificationtracker.h"
 #include "windows/rkmdiwindow.h"
 
 class RObject;
@@ -37,7 +38,7 @@
 
 @author Thomas Friedrichsmeier
 */
-class RObjectViewer : public RKMDIWindow, public RCommandReceiver {
+class RObjectViewer : public RKMDIWindow, public RCommandReceiver, public RObjectListener {
 Q_OBJECT
 public:
 	~RObjectViewer ();
@@ -48,13 +49,13 @@
 	void update ();
 	void toggleSummary ();
 	void togglePrint ();
-	void objectRemoved (RObject *object);
 protected:
 	friend class RKWorkplace;
 	RObjectViewer (QWidget *parent, RObject *object);
 
 	void rCommandDone (RCommand *command);
 	void closeEvent (QCloseEvent *e);
+	void objectRemoved (RObject *object);
 private:
 	QLabel *status_label;
 	QLabel *description_label;

Modified: branches/KDE4_port/rkward/windows/rkworkplace.cpp
===================================================================
--- branches/KDE4_port/rkward/windows/rkworkplace.cpp	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/windows/rkworkplace.cpp	2007-11-07 18:11:02 UTC (rev 2183)
@@ -299,40 +299,27 @@
 	RObject *iobj = object;
 	RKEditor *ed = 0;
 	RKEditorDataFramePart *part = 0;
-	RKEditor *existing_editor = object->objectOpened ();
+	RKEditor *existing_editor = RKGlobals::tracker ()->objectEditor (object);
 	if (!existing_editor) {
-		bool create_editor = false;
-		if (object->isDataFrame ()) {
-			create_editor = true;
-		} else if (object->isVariable () && object->getContainer ()->isDataFrame ()) {
-			existing_editor = object->getContainer ()->objectOpened ();
-			if (!existing_editor) {
-				create_editor = true;
-				iobj = object->getContainer ();
+		unsigned long size = 1;
+		for (unsigned int i = 0; i < iobj->numDimensions (); ++i) {
+			size *= iobj->getDimension (i);
+		}
+		if ((RKSettingsModuleGeneral::warnLargeObjectThreshold () != 0) && (size > RKSettingsModuleGeneral::warnLargeObjectThreshold ())) {
+			if (KMessageBox::warningContinueCancel (view (), i18n ("You are about to edit object \"%1\", which is very large (%2 fields). RKWard is not optimized to handle very large objects in the built in data editor. This will use a lot of memory, and - depending on your system - might be very slow. For large objects it is generally recommended to edit using command line means or to split into smaller chunks before editing. On the other hand, if you have enough memory, or the data is simple enough (numeric data is easier to handle, than factor), editing may not be a problem at all. You can configure this warning (or turn it off entirely) under Settings->Configure RKWard->General.\nReally edit object?", iobj->getFullName (), size), i18n ("About to edit very large object")) != KMessageBox::Continue) {
+				return 0;
 			}
 		}
 
-		if (create_editor) {
-			unsigned long size = 1;
-			for (unsigned int i = 0; i < iobj->numDimensions (); ++i) {
-				size *= iobj->getDimension (i);
-			}
-			if ((RKSettingsModuleGeneral::warnLargeObjectThreshold () != 0) && (size > RKSettingsModuleGeneral::warnLargeObjectThreshold ())) {
-				if (KMessageBox::warningContinueCancel (view (), i18n ("You are about to edit object \"%1\", which is very large (%2 fields). RKWard is not optimized to handle very large objects in the built in data editor. This will use a lot of memory, and - depending on your system - might be very slow. For large objects it is generally recommended to edit using command line means or to split into smaller chunks before editing. On the other hand, if you have enough memory, or the data is simple enough (numeric data is easier to handle, than factor), editing may not be a problem at all. You can configure this warning (or turn it off entirely) under Settings->Configure RKWard->General.\nReally edit object?", iobj->getFullName (), size), i18n ("About to edit very large object")) != KMessageBox::Continue) {
-					return 0;
-				}
-			}
+		part = new RKEditorDataFramePart (0);		// TODO: reverse creation logic, just as in the other classes!
+		ed = part->getEditor ();
+		// TODO: add child objects, too?
+		ed->openObject (iobj, initialize_to_empty);
 
-			part = new RKEditorDataFramePart (0);		// TODO: reverse creation logic, just as in the other classes!
-			ed = part->getEditor ();
-			// TODO: add child objects, too?
-			ed->openObject (iobj, initialize_to_empty);
-
-			ed->setCaption (iobj->getShortName ());		// TODO: move to editor
-			ed->setIcon (SmallIcon ("spreadsheet"));
-			addWindow (ed);
-			ed->setFocus ();		// somehow we need to call this explicitly
-		}
+		ed->setCaption (iobj->getShortName ());		// TODO: move to editor
+		ed->setIcon (SmallIcon ("spreadsheet"));
+		addWindow (ed);
+		ed->setFocus ();		// somehow we need to call this explicitly
 	}
 
 	if (existing_editor) {		// not strictly an else. existing_editor may be reset inside the above if

Modified: branches/KDE4_port/rkward/windows/rkworkplace.h
===================================================================
--- branches/KDE4_port/rkward/windows/rkworkplace.h	2007-11-05 23:20:42 UTC (rev 2182)
+++ branches/KDE4_port/rkward/windows/rkworkplace.h	2007-11-07 18:11:02 UTC (rev 2183)
@@ -111,6 +111,7 @@
 	void newX11Window (WId window_to_embed, int device_number);
 	void newObjectViewer (RObject *object);
 
+#warning remove initialize_to_empty param. This should happen for all pending objects, but else never.
 /** @returns true if there is a known editor for this type of object, false otherwise */
 	bool canEditObject (RObject *object);
 /** Creates a new editor of an appropriate type, and loads the given object into the editor


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